一段关于c语言的谈话

来源:互联网 发布:java 异或运算 编辑:程序博客网 时间:2024/04/30 20:43
Exceeding the 64K limit.

It's been a long time ago.  But in pascal, if I remember correctly we use to
be able to use pointers and GetMem to allocate memory from the heap for
arrays, records and such, use pointers ^ to access the data, then use
FreeMem(struct,len) the release the memory.  I also remember having to split
large units into smaller units in order to keep from running into 64K
issues.  Again, a very long time ago.

What is the preferred process in C to do this?

int MyIntArray[4096];  //standard ole way to get 4096 bytes off the stack.

far... what does this do?  From the best I can tell it puts it in a
different 64k memory segment or the heap?
int far MyIntArray[4096];

I take it I would leave the number of elements out and declare a pointer,
then allocate it,  use it, then free it when done.

int *MyIntArray[0];
MyIntArray = malloc(4096);
//Use array
MyInitArray[0] = 1;
//Print value or something
free(MyIntArray);  //Wow, I don't have to tell it how many bytes to release.
Cool.

//This puts it All in the heap and allows all of the main memory to be used
instead of just the stack?

So I'm confused.  I see far used in some programs but the docs do not say a
whole lot about it.  Says it's used for accessing memory in small models.?

Any help making my thick skull learn the proper way to handle global arrays,
and local arrays to be more effecient?

Thanks,
Richard

> int MyIntArray[4096];  //standard ole way to get 4096 bytes off the stack.

8192 bytes.  In a 16 bit program, an int is 2 bytes.

> far... what does this do?  From the best I can tell it puts it in a
> different 64k memory segment or the heap?
> int far MyIntArray[4096];

If this declaration is inside of a function there could be trouble with it.
Declarations of variables within a function are automatic variables
allocated from the stack (unless you modify it with a keyword such as
'static').  The default stack size is 4K and the maximum stack size is 64K
(actually 64K-16).  Be very sparing with array declarations within
functions.

The keyword 'far' tells the compiler and linker to place the variable in its
own segment.  While a segment can be used to address 64K that does not mean
the allocation is 64K.

There are two main uses for 'far':

- Used in an actual or 'extern' declaration of an array:

    A program has but one default data segment so is limited to < 64K
    of global and static memory.  By using 'far' you place the item
    outside of that 64K block so bypass that limitation.

- Used in a declaration of a pointer or on a calling parameter in a
function header or function prototype:

    In tiny, small and medium models the default pointer type is 'near'
    which means the address of the item is passed as a 16 bit offset
    into the default data segment, DGROUP.  By using 'far' you are
    specifying that the pointer is a full segment-offset address.

    In the other memory models the default pointer size already is in 32
    bit, segment-offset form so 'far' is not required.

    To handle the memory model differences for this situation the
    compiler provides a macro, 'FAR', which can be used so that what
    the compiler sees is appropriate for the memory model.

> free(MyIntArray);  //Wow, I don't have to tell it how many bytes to
> release. Cool.

The compiler's memory management system must know the size of each item it
allocates so that it can know what sections of the allocated memory blocks
it has gotten from the operating system are free or in use.  When you call
free() you pass it the pointer value, the address of one of the sub blocks
it has given you and it can then mark that block as unused.

> Any help making my thick skull learn the proper way to handle global
> arrays, and local arrays to be more effecient?

In 16 bit programs memory size and addressing constraints dominate.  Only
after you address those issues do you have the freedom to consider
efficiency.  As a general rule, only declare small arrays inside functions,
do that only when you absolutely must and consider the stack size when you
do.

One thing you MUST learn before playing around is how to adjust the size of
the stack.  The stack size is determined during the link phase by the value
of a global variable named '__stklen'.  It defaults to 4096, 4K, not enough
for any of what we are discussing.  You can alter that with ONE declaration
in ONE source file of your program.  The declaration might look like this:

  /* I recommend that this NOT be initialized to greater than 32K */
  extern unsigned __stklen = 32768U; /* note the 'U' - very important! */

.  Ed

- Hide quoted text -
- Show quoted text -

>> int MyIntArray[4096];  //standard ole way to get 4096 bytes off the
>> stack.

> 8192 bytes.  In a 16 bit program, an int is 2 bytes.

Yes, sorry, I typed it too fast.....  Thanks for the help.
I have less than 1000 bytes in arrays.  I went to add another 1024 bytes for
a buffer and got a warning that I exceeded the 64k limit for the current
segment or something.  So I figured this was the cause.  I got rid of a tub
load of string literals I was using for remote debugging  (no monitor or
keyboard) and that helped.  My program really isn't that big and I paid
close attention to declare my variables as small as possible, with most
being char and a few being int.  I put most repetitive type stuff that
contain string literals into a loop so only one string literal actually
existed in order not to show the same string literal many times.

I saw a couple of options in the compiler which seems that they should be
set by default.
Put constant strings into code segment,  Far virtual tables,  Automatic Far
Data.
Any reason I wouldn't want these on by default for a Large Memory model, 16
bit DOS on a machine with a total of 512K ram?


> I saw a couple of options in the compiler which seems that they should be
> set by default.
> Put constant strings into code segment,  Far virtual tables,  Automatic
> Far Data.
> Any reason I wouldn't want these on by default for a Large Memory model,
> 16 bit DOS on a machine with a total of 512K ram?

Start the IDE without any project selected.  Set the options you like.  Save
all and I think the default options will then be as you selected.

Unless you are using multiple source files, you probably want to avoid
putting constant strings into the code segment.  In large model, each source
file has its own code segment so you can have 64K times the number of source
files of code.  However if you have only one source file then you only have
one code segment so the strings can take from the allowed amount of code.

Far virtual tables have no meaning in C programs, only in C++ ones in which
classes are used.  Perhaps I made a mistake but I read your messages as
saying that you are using C.  (If using C++, then why are you using 'malloc'
instead of 'new' and how do you get away with the assignment of the return
from 'malloc' to a pointer variable whose type is not 'void*' without using
a cast?)

I think Automatic Far Data is a close to useless option.  You end up with
each item placed into its own segment and in a program of size soon run up
against the number of segments that the linker can handle.  It is easier
just to say 'far' in the declarations of large global and static arrays.

Please again read the description in my last message for setting the stack
size.  I think it is important that you use it.

.  Ed

Yes,  I'm using the plain ole C.

So, if I understand what your saying, I can break my source into mutiple
units  (source files) and solve some of these issues.  Maybe move some of my
string constants and functions that use them into their own source file and
those would go into their own segment.

Right now, I'm not well enough versed and I'm most likely doing a lot of
stuff wrong.  I do not have a header for my main source.  I have all my
globals and types defined in the top section.  If I did something like
create a source file and header to put all my global variables, arrays, and
init routines in there and get it away from my main would that also break
things up?

If you selected the option to put constant strings into the code segment and
are using large model then yes, scattering those strings among different
source files will reduce the size used from each code segment.  There will
be a need to place an 'extern' declaration into the project header file(s)
for each constant string which is used in a different source file than the
one in which it is declared.

We have discussed some concepts which are somewhat advanced but your
comments now suggest that you truly are beginning in C.  For the time being
table the issues you have been asking about and attack these items.  You can
return to the issues afterwards.

Move a couple of functions from your one source file into another or others
so that your project now has more than one source file.  Add the new source
file(s) to the project.  Now create a header file for the project.

The project header is a text file containing prototypes and extern
declarations for all global items in the project.

A header file should contain what are commonly called 'header guards'.  They
guard against multiple declarations when one header includes another which
has already been included.  Header guards look like this:

---------start of the header file----------
#ifndef  SOMENAME_H
#define SOMENAME_H

    -----contents of the header file go here-----

#endif
---------end of the header file----------

The name of the macro is not that important.  If the header file name is
somename.h then I make the macro SOMENAME_H.  Macros are traditionally all
upper case.

Assume that you have a global int named 'item_count'.  You do a normal
declaration for it in a source file, probably like this:
  int item_count;

In the header file you then place an external declaration like this:
  extern int item_count;

The effect is that when compiling any source file which includes the header
the compiler will know of that variable.

Similary you can do that for functions although for them the 'extern' is
optional and rarely used.  Such a thing is called a 'function prototype'.
For example:

Assume your source code has this function:
    int Add7ToValue(int value)
        {
        return value + 7;
        }

The function prototype for it might look like this:
    int Add7ToValue(int);
           or
    int Add7ToValue(int value);

I like the second version of the function prototype because the name of the
calling argument usually documents something about what it is and/or how it
is used.  That may seem obvious but get into a larger project where you
spend weeks or more writing code and your memory of the details of what you
did a while ago might not be as apparent as you think they will be.

You should make a serious effort to minimize the number of global variables.
Any global variable or function which is used in only one source file should
be decorated with the keyword 'static'.  That limits the visibility for that
item to only in that one source file and it should not appear in a project
header file.  I call such things 'file global' since they are global only to
the one source file.  Also look to what variables are file global but can be
made local to a functon.  Globals are the source of some of the more subtle
bugs in code and should be minimized.

In general project header files should be included AFTER the includes of the
system (compiler-supplied) header files.

And, as a general rule, programs which have large numbers of string
constants and string constants of large size are not well written programs.
Yes there are exceptions but carefully examine how your are using those
constants to see if you really need to use all of that.

.  Ed

Okay,  Say my Main source file is called user.c then I could create a file
called user.h and put my Macro up top for USER_H so it's only brought in
once.

All the prototypes from user.c get moved to user.h
All the global variables can be moved into User.h.  Now, since these global
vars only get used in user.c only, I don't have to mark them as external do
I?  Would they be better fitted as static?

Next, what about regualar constants, and structs, typedefs and macros.  Move
those all over as well?
So basically the only thing user.c will contain is my stack statement to set
the size of the stack, and the includes for the headers I'm bring in.

Then next,  I can move functions over to their own source files grouped
together.  Like for example, since I do not have a standard clock, I have
several funtions for the Clock and date conversions.  I can put them in a
unit/source file called MyClock.c, then create a MyClock.h and put their
variables and prototypes in it.

Do I understand this right before I butcher stuff to bad?

Thanks again Ed.
Richard

Ah,  I re-read you previous email.  Global vars for that source only get
marked as static, and do not go into the header file, but into the source
which they are used in.

Okay, I did all that you mentioned. Increased the stack, added the  Header
files and broke it up into a few sources.  Built fine, ran fine.

Now, I'm Just testing.... so for grins I added a test array in my user.h
(main header file) like this...
WORD MyTestArray[4096];  //8 kbytes
Then the compiler gives me DGROUP Exceeds 64K

So, would that mean I need to use malloc on these so they go on the heap?

Richard

"Richard" <rwskinner@ccwipDOTnet> wrote in message

This seems to work...

WORD *TestWords;

  //Test  works fine for heap Tested with 50,000 bytes for grins
  if ((TestWords = (WORD*)malloc(50000)) == NULL)
  { exit(1);  //Out of Memory
  }
  TestWords[1] = 1024;
  //prints that element fine.

I tried it with the cast (WORD *) and without a cast and both worked fine ??
Should it be casted or does the compiler know?

  if ((TestWords = (WORD*)malloc(50000)) == NULL)
  if ((TestWords = malloc(50000)) == NULL)

Richard wrote:
>Now, I'm Just testing.... so for grins I added a test array in my user.h
>(main header file) like this...

Nothing ever goes into header files except prototypes.
#define, extern, structure types, are prototypes.
Code and storage are not.
Note that a #define containing code does not actually create code when
read (header is fine), only when invoked.

>WORD MyTestArray[4096];  //8 kbytes

That is storage and does not belong in a headerfile.
What happens is you end up with an 8k array in every unit that includes that header.

WORD *MyTestArray;
is also data, and you get a different pointer in each unit.
(Each unit has its own pointer, only one of which is likely
to contain the real pointer to the allocated array.)

Headers should only use the data prototypes:
        extern WORD MyTestArray[4096];
or
        extern WORD *MyTestArray;

Then one-and-only-one unit contains the actual data, just as one-and-only-one
unit contains the actual code for a function.

static:
The static keyword modifies the visability (scope) of a variable.
Static variables are "global" in time, but "local" in scope.
Static inside a function is only known inside that function.
Static inside a unit is a "global" that is only available inside that unit.
Static in a header is mostly nonsense.


Thanks Bob,  I'm pretty sure your explanation of static sunk in.

Guess this is where my pascal starts bleeding thru.  Interface portion of
source files are global,  Implementation section is staitc to that unit,
vars declared in Procs, Functions are automatically local to just that proc.
or function.

So this example, the var is declared and initialized inside MyFunc each time
MyFunc is called, and does not exist (for use) outside MyFunc.  So
basically, a new variable is declared in mem each time and must be
initialized.

void MyFunc(void)
{ int x;
   x = 3;

}

So this example, the var is declared as static and is modified only inside
MyFunc and retians it's value only inside of MyFuct, and does not exist (for
use) outside MyFunc.

void MyFunc(void)
{ static int x;
   x++;
   //x = increments last value of x from the last run of this function each
time I enter this function

}

In the Top of a Source file
int x;  //x is global for the entire project and really should be in the
header
static int x;  //x is global for this source only and should be in the
source.

constants such as below should only be used in source files only
const x = 3.24;

But, if we wanted a global constant for our entire program, This would go
into the header or preferably a header called MyConst.H

const version = 1.01;

"Bob Gonder" <no...@notmindspring.invalid> wrote in message


Richard wrote:
>void MyFunc(void)
>{ static int x;
>   x++;
>   //x = increments last value of x from the last run of this function each
>time I enter this function
>}

I would just add this:
int MyFunc(void)
{ static int x = 0;     // initialized only the first time
   ++x;         // modified each time
   return x;    // returns 1, 2, 3...

}
>In the Top of a Source file
>int x;  //x is global for the entire project and really should be in the
>header

        Should also be "extern" in the header.

>constants such as below should only be used in source files only
>const x = 3.24;

Requires a type:
        const float x = 3.24;

>But, if we wanted a global constant for our entire program, This would go
>into the header or preferably a header called MyConst.H
>const version = 1.01;

Again, needs type, or macro:

#define version 1.01


Hi Richard

 Richard says:

> In the Top of a Source file
> int x;  //x is global for the entire project and really should be in the
> header
> static int x;  //x is global for this source only and should be in the
> source.

No, it's more like the other way arround

int x; 
at the top of a cpp file is visible in that file ONLY.
if You want it to be more then that You have to add
it to the .h file as well

extern int x;

p.s. it has to be in both the cpp and the h file.

static int x;

is global, but only accesible where it can be seen.

so writing static int x; at the top of a cpp file
dont make much sence infact using the word static
outside a set of {} dont make much sence.

As the word static is used to prolong the life of
int x to the lifetime of the process.
And int x; declared at the top of a cpp file have
that lifespand anyway.

Kind regards
Asger

Asger Joergensen wrote:
>No, it's more like the other way arround
>int x; 
>at the top of a cpp file is visible in that file ONLY.

Uh, no.
Unless a file-scope variable is static, it is exported to the linker.
Thus any other unit that wants to use it can just declare an extern to it.
It doesn't have to be in an h file.
Header files aren't magic.

The same applies to functions.
If you don't want helper functions polluting the link, make them static as well.

>if You want it to be more then that You have to add
>it to the .h file as well

>extern int x;

>p.s. it has to be in both the cpp and the h file.

Not the "extern" keyword, only needs to be in the header.

>static int x;

>is global, but only accesible where it can be seen.

>so writing static int x; at the top of a cpp file
>dont make much sence infact using the word static
>outside a set of {} dont make much sence.

Makes a lot of sense (if you like to use globals).
Otherwise, the linker will complain when multiple
units have the same "local globals".

>As the word static is used to prolong the life of
>int x to the lifetime of the process.
>And int x; declared at the top of a cpp file have
>that lifespand anyway.

"static" outside a code block can be more understood
as meaning "private", do not export.

Hi Bob

Thanks for putting me straight.

One part of me is very sory that I added to the
"global" confucion another part of me is happy that
i did otherwise I wouldn't have know that i could
make a global variable private to a file..

I would have liked though, if they had chosen another
word instead of static, maybe used the word private
instead, fits better with my Danish carpenter logic.

p.s. could You explaine a little about what You mean
by:

> If you don't want helper functions polluting the link

Thanks again
Kind regards
Asger

Asger Joergensen wrote:
>p.s. could You explaine a little about what You mean
>by:
>> If you don't want helper functions polluting the link

Unit1.c

int A(int i)    {return i*2}
int B(int i)    {return i*3}
int C(int i)    {return i*4}

int MyFunc( int i )
{       return A(B(C(i)));

}

Unit2.c

int A(int i)    {return i+2};
int B(int i)    {return i+3};
int C(int i)    {return i+4};

int MyOtherFunc(int i )
{       return A(B(C(i)));

}

The linker will complain that A, B and C are duplicated.
Since they are private subfunctions (we realy only want
other units to use MyFunc), we can make them static, and
they disappear from the link.

static int A(int i)     {return i+2};
static int B(int i)     {return i+3};
static int C(int i)     {return i+4};

About "disappearing from the link":
The compiler knows about all the functions in a single unit.
It can therefore code the relative addresses of the local functions.
The job of the linker is to fill in the addresses of external
functions and variables.
By making the functions private to the unit (with static),
their names are removed from the list of public symbols,
and the linker never knows they exist.


Every object file contains lists.  Among them are:
- A list of public symbols that the items in the file supply
- A list of external references (external symbols) which
  the file uses but does not have.

Part of what the linker does is to search object files and libraries to
satisfy the unresolved external references.  In doing this it maintains a
list of those symbols which it has already found.

When you declare a global variable or function the name of that item is
added to the public symbols list.  This is independent of if the item is
mentioned in a header file.  The header file does not exist during the link
phase or even during the compile phase.  It dissapears after the
preprocessing phase.

If more than one object file contains a given symbol the linker will fail
with a duplicate symbol error.

Because of that it is prudent to mark all global variables and functions in
a source file that are not used by other source files with the keyword
'static' to remove them from the list of globals and hence remove their
symbol from the public symbols list.

In other words, it is not appropriate to decide if something is to be
labeled as static.  What is appropriate is to decide if it is to NOT be
labeled as static.

These issues do not apply to members of a C++ structure or class.  For them
the keyword 'static' has a different meaning.

.  Ed
 
原创粉丝点击