LinuxQuestions.org
Help answer threads with 0 replies.
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 05-07-2012, 04:40 AM   #1
deadeyes
Member
 
Registered: Aug 2006
Posts: 609

Rep: Reputation: 79
C programming: memory use?


Hi all,

I am busy with writing some simple apps and have some question.

I defined a struct entitysize {
char * str1;
char * str2;
int nr;
}

In a function1 I allocate memory to hold 5 of these structs. The pointer to the allocated memory is a global variable(varfilelist).

In function2 I initialize all the structs:
Code:
void init_file_dir_lists() {
  // Init dir and file list
  int size = 0;
  int i;
  struct entitysize * ptrentity;

  ptrentity = varfilelist;
  for (i=0; i<hitlistsize; i++) {
    ptrentity->entity = "test"; 
    ptrentity->path = "test"; 
    ptrentity->size = size;
    ptrentity++;
  }
}
In another function I will be changing these pointers in the structs.
Now my question

As soon as I change the pointer... what will happen with the memory that is allocated for the string "test"?
Will this be lost somewhere? Like if you didn't free dynamically allocated memory?

Thanks in advance!
 
Old 05-07-2012, 06:02 AM   #2
johnsfine
LQ Guru
 
Registered: Dec 2007
Distribution: Centos
Posts: 5,286

Rep: Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197
The code you quoted isn't consistent between the parts, which makes it hard to be sure what you mean, but I think I can explain the key concept you seem to be missing:

Quote:
Originally Posted by deadeyes View Post
struct entitysize {
char * str1;
char * str2;
int nr;
}
...
struct entitysize * ptrentity;
...
ptrentity->entity = "test";
I assume you mean you are assigning a text constant to a structure member that was declared char*
It is unfortunate that C allows that. A text constant ought to be assigned only to pointers declared char const*

When you make that assignment, no new memory is allocated, nor text copied. You are just setting a pointer.

The memory for a text constant such as "test" is allocated at compile time. The instruction you quoted just sets ptrentity->entity to the address of that compile time allocation.
Quote:
In another function I will be changing these pointers in the structs.
Now my question

As soon as I change the pointer... what will happen with the memory that is allocated for the string "test"?
Nothing.

You used that text constant "test" inside a loop inside a function. Every time that instruction is executed, it uses the address of the same text constant. It does not allocate anything at run time.

You also used identical text constants "test" in more than one place. The compiler may choose to use the same text constant for both.
 
Old 05-07-2012, 08:29 AM   #3
sundialsvcs
LQ Guru
 
Registered: Feb 2004
Location: SE Tennessee, USA
Distribution: Gentoo, LFS
Posts: 10,633
Blog Entries: 4

Rep: Reputation: 3931Reputation: 3931Reputation: 3931Reputation: 3931Reputation: 3931Reputation: 3931Reputation: 3931Reputation: 3931Reputation: 3931Reputation: 3931Reputation: 3931
You said that str1 is a pointer to an array of characters. You then assigned one of those pointers to contain (of course...) the address of a static string array, allocated by the compiler, which contains: [ 't', 'e', 's', 't', '\0' ]

Remember that "C" is by-design a very low-level language; not much more by-default than assembler. "C++" builds on top of it, partly through the use of a much larger runtime library and more expressive semantics. ("C++" of course is built in "C.") If you are accustomed to other programming languages (all of them, of course, originally written in "C"), you can be misled by thinking that you see in "C" what is not there.

Every programming language or tool is the same.
 
Old 05-07-2012, 01:18 PM   #4
deadeyes
Member
 
Registered: Aug 2006
Posts: 609

Original Poster
Rep: Reputation: 79
Thanks for your response! It certainly made a few things clearer!

Currently I am using the readdir function to get file entries in a directory.
For each file I use stat to get the filesize.

If the size is in the top 5(in the structs located in the dynamically allocated memory) it gets put in the allocated memory.
If this is the new biggest file, I walk over all structs and move the pointers to change the order and place the new biggest file at the end.

Now for the smallest one, this would fall off(the pointers in the struct in the beginning are overridden to point to the new smallest in the top 5). What I wonder is if I actually loose the memory for that smallest entry (memory leak) or not as it is only the struct that is dynamically allocated.
All the rest are char *.

However it could be that readdir/stat allocate memory themself and I need to free that space...
In that case I would waste alot of memory when going over 1000s of files.

Thanks again!
 
Old 05-07-2012, 02:26 PM   #5
johnsfine
LQ Guru
 
Registered: Dec 2007
Distribution: Centos
Posts: 5,286

Rep: Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197
Quote:
Originally Posted by deadeyes View Post
Currently I am using the readdir function to get file entries in a directory.
Some relevant info from the readdir(3) man page:
Code:
    char           d_name[256]; /* filename */
...
The data returned by readdir() may be overwritten by
subsequent calls to readdir() for the same directory stream.
So the name you get from there is in the struct, not pointed to by the struct. To keep that name past the next call to readdir, you must copy it somewhere. How that memory is allocated is up to you. If you malloc it, you should later free it to avoid a memory leak.

Quote:
it gets put in the allocated memory.
This seems to be one of those times that English is not effective in describing C code. Sometimes the only effective description of C code is the actual C code.

Quote:
However it could be that readdir/stat allocate memory themself and I need to free that space.
Things like that would be clear in the man page. You can see in the readdir man page that you are not responsible for freeing the pointer you get from readdir and you are responsible for copying the data to somewhere else if you want to keep the data past the next readdir.

Last edited by johnsfine; 05-07-2012 at 02:34 PM.
 
Old 05-07-2012, 03:42 PM   #6
Nominal Animal
Senior Member
 
Registered: Dec 2010
Location: Finland
Distribution: Xubuntu, CentOS, LFS
Posts: 1,723
Blog Entries: 3

Rep: Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948
Quote:
Originally Posted by deadeyes View Post
Currently I am using the readdir function to get file entries in a directory.
For each file I use stat to get the filesize.
As the readdir man page states:
Quote:
Originally Posted by readdir(3)
The data returned by readdir() may be overwritten by subsequent calls to readdir() for the same directory stream.
In other words, temporary storage is allocated as part of the directory stream (DIR *), and reused for each readdir() call; the string is only valid up to the next readdir() or closedir() call referring to the same directory stream. (Actually, the temporary storage tends to be a bit larger, large enough to hold a few dozen directory entries at least.)

Personally, I'd use a C99 structure similar to the following:
Code:
struct fileinfo {
    /* File properties would go here,
     * except for your sort key, file size.
    */

    /* File path and name */
    size_t           name;
    size_t           pathlen;
    char             path[];
};

/* Full path accessor function. Returns none if given an invalid pointer. */
static inline const char *fileinfo_path(const struct fileinfo *const info, const char *const none)
{
    if (info && info->pathlen)
        return (const char *)info->path;
    else
        return none;
}

/* File name accessor function. Returns none if given an invalid pointer.
*/
static inline const char *fileinfo_name(const struct fileinfo *const info, const char *const none)
{
    if (info && info->pathlen)
        return (const char *)info->path + info->name;
    else
        return none;
}

/* Free a fileinfo structure.
 * Always returns NULL.
*/
struct fileinfo *fileinfo_free(struct fileinfo *const info)
{
    if (info && info->pathlen) {
        /* Poison the structure.
         * This helps catch use-after-free,
         * as the path is suddenly empty.
        */
        info->pathlen = 0;
        info->name = 0;
        info->path[0] = '\0';

        /* Free the dynamically allocated memory
         * used by the structure. */
        free(info);
    }
    return NULL;
}

/* Construct a fileinfo structure.
 * Does NOT fill in the file properties (size et cetera)!
*/
struct fileinfo *fileinfo_create(const char *const path, const char *const name)
{
    const size_t     pathlen = (path) ? strlen(path) : 0;
    const size_t     namelen = (name) ? strlen(name) : 0;
    const size_t     padded  = ((pathlen + namelen + 1) | 7) + 1;
    size_t           n;
    struct fileinfo *info;

    if (pathlen + namelen < (size_t)1) {
        errno = EINVAL;
        return NULL;
    }

    info = malloc(sizeof (struct fileinfo) + padded);
    if (!info) {
        errno = ENOMEM;
        return NULL;
    }

    /* Make sure the path is terminated with an ASCII NUL. */
    if (padded > 16)
        memset(info->path + padded - 16, '\0', 16);
    else
        memset(info->path, '\0', padded);

    /* Build the actual path. */
    n = 0;

    if (pathlen > 0) {
        memcpy(info->path, path, pathlen);
        n += pathlen;
        if (info->path[n-1] != '/')
            info->path[n++] = '/';
    }

    info->name = n;

    if (namelen > 0) {
        memcpy(info->path + n, name, namelen);
        n += namelen;
    }

    info->pathlen = n;

    return info;
}
To keep a fixed number of fileinfo structures, create an array of fileinfo pointers, and another array of file sizes (off_t type), with the same number of entries. The separation into two arrays makes sure your CPU does minimal memory accesses to check the file sizes.

For each new directory entry, obtain its file size, and compare against the initial entry in the file size array. If the new file is larger, discard the initial fileinfo structure, and create a new for the new directory entry. Also update the initial file size array entry to reflect the file size. Then, scan through the file size array to see if there is a smaller file size than the new initial entry. If so, swap the two, so that the entry corresponding to the smallest file is always first in the arrays.

Note: you do not need to do more than one swap, total. Scan the array first, then do one swap, if necessary. This is also why the sort key, file size, array is separate: so that it can be scanned with minimal CPU use. For small arrays (depends on CPU, but say up to a couple of dozen entries) this is the fastest option.

The above is reasonably fast for several dozen or even hundreds of entries, but it scales poorly when the arrays get much bigger. In other words, with enough entries in the array, it gets slow.

If you wanted to keep a lot of fileinfo structures, order the arrays as a binary min heap. Element at index i has parent (with equal or smaller filesize) at index (i-1)/2, and possibly childs (with equal or larger filesizes) at indices 2*i+1 and 2*i+2.
The entry corresponding to the smallest known file is at index 0, so most of the time, you'll be simply comparing against that one. When you replace it, you percolate the entry outwards, swapping with the smaller child, until the heap property (parent filesize <= filesize <= child filesize) is no longer violated. This is just log₂N steps even in the worst case, so this is very fast even if the array size N gets very large. (To append to the heap, you add the data at the end, then percolate down, swapping with the parent until the parent is smaller or equal in file size.)
 
  


Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
Memory Access Violation - C Programming richmike3 Programming 24 09-23-2009 02:00 AM
Programming Game(s), Refresh my memory :) indeep2k5 Linux - Games 3 08-09-2009 01:03 PM
network and virtual memory programming wangjinyi Programming 7 01-08-2008 09:54 AM
LXer: Improve Your Memory Programming LXer Syndicated Linux News 0 07-06-2007 12:16 AM
Memory problems in Xil programming slowman Programming 1 06-08-2003 12:33 PM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 03:03 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration