简体   繁体   中英

pointer without a malloc

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>

int main(){
        struct stat *something;
        stat("/etc/profile", something);
        printf("%d\n", something->st_gid);
        free(something);
        return 0;
}
$ ./a.out
Segmentation fault

I learned from this post that I need to allocate memory using malloc, so I changed it to as below, and it works:

-       struct stat *something;
+       struct stat *something = malloc(sizeof(struct stat));

In a prior but related exercise, I did not use a malloc, and it had worked. I am lost! Why don't I need malloc for the line " *struct dirent b; " below?

Or, to rephrase, how can we know the payload is too much and to use malloc?

#include <stdio.h>
#include <dirent.h>

int main(int argc, char *argv[]){

        if (argc != 2){
                printf("Error. Syntax: ls <somefolder> \n");
                return 1;
        }
        DIR *a = opendir(argv[1]) ;
        if (a == NULL){
                printf("error. cannot open %s\n", argv[1]);
                return 1;
        }

        // - malloc question about this very next line
        struct dirent *b; 
        while (( b = readdir(a)) != NULL){
                printf("%s %lu\n", b->d_name, b->d_ino);
        }

        int closing = closedir(a);
        printf("in closing, status is %d\n", closing);
        return 0;
}

Newcomer to C, clueless too - please be gentle! :)

The problem with

struct stat *something;
stat("/etc/profile", something);

is that something is an uninitialized pointer pointing to nowhere, this yields undefined behaviour as stat would write something on an invalid address. With malloc you allocated memory for it and passed a pointer to that allocated memory location to stat and that's why it worked.

But you don't need to use malloc for that, just don't declare something as a pointer:

struct stat something;
stat("/etc/profile", &something); // <-- look at the usage of &

In stat you should use the & -operator which returns a pointer to something .

In your other program

struct dirent *b; 
while (( b = readdir(a)) != NULL)

readdir returns a pointer to a valid location, the function itself took care of using a valid object and returning a pointer to it. However, you cannot do free(b) :

man readdir

RETURN VALUE

On success, readdir() returns a pointer to a dirent structure. (This structure may be statically allocated; do not attempt to free(3) it.)

readdir returns a pointer to a struct dirent . Doing: b = readdir(a) overwrites the value b with the new value returned by readdir .

So of b was previously initialized with allocated memory from a malloc call, that value has been overwritten and you now likely have a memory leak.

You might wonder then whether you need to call free on b after the readdir call. To answer that you have to consult your documentation. In this case, the answer is no .

From the documentation for readdir :

On success, readdir() returns a pointer to a dirent structure. (This structure may be statically allocated; do not attempt to free(3) it.)

int main(){
        struct stat *something;
        stat("/etc/profile", something);
        printf("%d\n", something->st_gid);
        free(something);
        return 0;
}

Above code has multiple issues,

  1. you are using something which is not pointing to a valid location, and again using it as a buffer to store information return by stat() .

int stat(const char *pathname, struct stat *statbuf);

stat() return information about a file, in the buffer pointed to by statbuf , so your statbuf (something) should be a valid buffer, with size enough to store information of file, ie size of struct stat .

  1. free(something);

from free() man page

if the argument passed to free() does not match a pointer earlier returned by a function in POSIX.1‐2008 that allocates memory as if by malloc(), or if the space has been deallocated by a call to free() or realloc(), the behavior is undefined.

Now coming to other code:

// - malloc question about this very next line
        struct dirent *b; 
        while (( b = readdir(a)) != NULL){
                printf("%s %lu\n", b->d_name, b->d_ino);
        }

let's explore readdir() a bit,

struct dirent *readdir(DIR *dirp); The readdir() function returns a pointer to a dirent structure representing the next directory entry in the directory stream pointed to by dirp. It returns NULL on reaching the end of the directory stream or if an error occurred.

You see readdir() returns a pointer of struct dirent type which makes b in struct dirent *b a valid pointer. That's why it worked.

Normally any pointers whether its a int *ptr or char *ptr or struct student *ptr , it should have valid address and valid address may be dynamically allocated address or address of some variable .

Case 1 : struct stat *something; here something is pointer of struct stat type and its un-initialized.

when you do stat("/etc/profile", something); you are trying to store /etc/profile information into something which is un-initialized. Instead of this you should store the information of /etc/profile into address of something or

struct stat info;
struct stat *something = &info;
stat("/etc/profile",something );

Case 2 : struct dirent *b; b is pointer of type struct dirent , it should also have valid address. b = readdir(a); readdir return value is assigned to b , so now you can perform b->d_name etc.

From the manual page of readdir()

On success, readdir() returns a pointer to a dirent structure. (This structure may be statically allocated; do not attempt to free(3) it.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM