Live data from Hacker News

Massacring C Pointers

wozniak.ca

21–30 of 300 posts

Re: Massacring C Pointers

#21

Yesterday I encountered a similar program on a HN comment chain as shown in this link. I am genuinely confused as to why this program is bad. I am a student and I do not know the best practices regarding pointers, but it is how I would write a program to combine two strings. Can someone please elaborate why it is bad? Are their any good resources to fill gaps in my knowledge? Thanks in advance. Edit: Thank you guys f…

There are multiple problems with that code: 1) return(r) He is returning pointer to temporary! When you declare and initialize variables on stack after that function, they will overwrite memory pointed by r. 2) He is assuming size of string pointed by s(including 0) is less than 100 and also the combined sizes of s and t are less than 100. Stack Overflow! 3) Not an errors, but would not pass my code review: Inconsistent Variable declaration, using signed integer for loop where you don't need signed. Also I would have used strcpy 2 times, or loop 2 times, not two different ways. Beside that, you should not use strcpy, but strncpy to avoid stack overflows.

Re: Massacring C Pointers

#22
post #12

Earlier quoted context omitted.

From a purely safety-minded perspective, this function has a hidden bound of r[] past which the function becomes unsafe, but it does not check. Not only does it not check, the design of the function means that there is no possible way for it to check safely. s and t are both pointers to characters? How long are the strings they might represent supposed to be? Who knows? This code is incredibly reckless and it's dange…

I'm sorry, but I don't think you've quite gotten the reasons why it was lambasted: > Not only does it not check, the design of the function means that there is no possible way for it to check safely. s and t are both pointers to characters? How long are the strings they might represent supposed to be? strlen > This leads into the second problem and one that is more subjective: this code is incredibly dense and relies…

strlen is not safe and the design of this function does not permit it to ever be safe. That is my point.

Most "nice" C functions are also not used as teaching examples. There's a difference in how one writes C code for production use and instructive use.

Re: Massacring C Pointers

#23

Make sure to click the link at the end of the article for code samples with potential for Segmentation fault:core dumped in 4 lines. Even more concerning is the book seems to have some positive reviews on Amazon(!), and just one shredding it.

The example given at the top of the article can cause a segmentation fault. See the other comments here for how.

Re: Massacring C Pointers

#24

Yesterday I encountered a similar program on a HN comment chain as shown in this link. I am genuinely confused as to why this program is bad. I am a student and I do not know the best practices regarding pointers, but it is how I would write a program to combine two strings. Can someone please elaborate why it is bad? Are their any good resources to fill gaps in my knowledge? Thanks in advance. Edit: Thank you guys f…

It is wrong in many ways.

It copies s and then t to a fixed size buffer, without any checks. That will write to invalid memory (probably smashing the stack) if len(s), len(t) or len(s) + len(t) > 100.

It returns a stack allocated buffer (r) pointer to the caller. The array will be invalid when the function returns, as the automatic variables only live in the function scope (during the call), they are deallocated when the function returns.

To do this right you have various strategies.

1. Allocate a buffer of len(s) + len(t) + 1 with malloc, copy the strings and return it. Have the caller free it when it's done. It can be inapropiate because of the dynamic allocation.

2. Have the caller pass a destination buffer and its size. If you know the char *'s are zero terminated, check if you have space for them in the dest buffer. If not, truncate or error out. Most of the times, this is the prefered solution.

3. Use a static local buffer, and return it to the caller. You may need to truncate the copy too. Not recomended. This is not a good solution as the function also will not be reentrant (unsafe with multiple threads).

You can use libc functions like strncpy (C89+), snprintf (C99+) etc to make a "size-checked" copy with various automatic truncation semantics. You can refer to their man pages for details.

Edit: to the downvoters, please point out what is wrong in the comment.

Re: Massacring C Pointers

#25
post #22

Earlier quoted context omitted.

I'm sorry, but I don't think you've quite gotten the reasons why it was lambasted: > Not only does it not check, the design of the function means that there is no possible way for it to check safely. s and t are both pointers to characters? How long are the strings they might represent supposed to be? strlen > This leads into the second problem and one that is more subjective: this code is incredibly dense and relies…

strlen is not safe and the design of this function does not permit it to ever be safe. That is my point. Most "nice" C functions are also not used as teaching examples. There's a difference in how one writes C code for production use and instructive use.

I take it you come from a higher level language, where null termination would seem risky. In C, however, strlen is considered safe (as opposed to say strcpy, strcat, etc. which do have "safe" replacements). As for terse examples being given to beginners, here's the example The C Programming Language gives for strcpy:

    void strcpy(char *s, char *t) {
    	while ((*s++ = *t++) != '\0') ;
    }

Re: Massacring C Pointers

#26

It is practically impossible to teach good programming to students that have had a prior exposure to BASIC: as potential programmers they are mentally mutilated beyond hope of regeneration. - Dijkstra, "How do we tell truths that might hurt?" (1975) The author, Traister, has came from BASIC. We have the explanation.

Well that is a dumb and inaccurate statement if ever I've read one. I mean I'm sure there might have been some nugget of truth back in the 70s in that it might have been frustrating to teach C or FORTRAN to BASIC programmers but it has as much relevance these days as any of the other memes people like to quite from yesteryear. I certainly managed the transition from BASIC to Pascal with ease and definitely follow good programming idioms. I also know a lot of others followed similar path of learning to myself.

Re: Massacring C Pointers

#27

Yesterday I encountered a similar program on a HN comment chain as shown in this link. I am genuinely confused as to why this program is bad. I am a student and I do not know the best practices regarding pointers, but it is how I would write a program to combine two strings. Can someone please elaborate why it is bad? Are their any good resources to fill gaps in my knowledge? Thanks in advance. Edit: Thank you guys f…

Most of these points are covered by the other comments. As a C programmer professionally, I'll go into a little more depth, and offer an alternative implementation for comparison.

The function in question:

    char *combine(s, t)
    char *s, *t;
    {
          int x, y;
          char r[100];

          strcpy(r, s);
          y = strlen(r);
          for (x = y; *t != '\0'; ++x)
               r[x] = *t++;
          r[x] = '\0';
          return(r);
    }
1. The array 'r' is allocated on the stack, and returned from the function. This is bad because 'r' goes out of scope as soon as the function is returned. This function returns a pointer to memory with essentially unknown contents.

2. The array 'r' is allocated at a fixed size. This is okay if you know ahead of time that you know this size, however for this function we don't know the lengths of s and t, so the odds that we are allocating the right amount of memory is slim.

3. As a result of points 1 and 2, the strcpy and loop may cause a buffer overflow. This is a class of bug where it is possible to overwrite memory that should be unavailable to us. In this case, if the combined lengths of s and t happen to be equal or greater than 100 characters, we will be overwriting memory that does not belong to r, corrupting it, and potentially crashing the program. This may additionally be as security risk, as buffer overflows can be exploited to execute malicious code.

4. There are no checks to see whether s and t are valid pointers. If they are NULL then the function would generate a segmentation fault. This is a check that is often ignored in cases where it is deemed to potentially hinder performance if the function is used frequently.

saulrh also mentions the case where s or t are not NUL-terminated. This is often considered to be a pre-condition of the function in C, suggesting that providing the function strings that aren't NUL terminated is an issue for the user.

For some comparison the following would be my first cut at the same function. Note I the comments are only for illustrative purposes. I'd omit them in actual code.

    char* combine(const char *s, const char *t)
    {
        size_t slen, tlen;
        char *str;
        
        // optional checks for validity (point 4)
        if (NULL == s || NULL == t)
        {
            return NULL;
        }
        
        // get lengths of s and t, to calculate allocation size
        // also save them to use with memcpy later
        slen = strlen(s);
        tlen = strlen(t);
        
        // allocate to heap (point 1)
        // allocate correct size (point 2)
        str = malloc(slen + tlen + 1);
        if (NULL == str)
        {
            return NULL;
        }
        
        // use memcpy since we already know the size
        memcpy(str, s, slen);
        memcpy(str + slen, t, tlen);
        str[slen + tlen] = '\0';
        
        return str;
    }

Re: Massacring C Pointers

#28
post #17

Earlier quoted context omitted.

I haven't touched C in years, but here's my descending "wtf" list: 1. Returns pointer to stack-allocated data, which immediately becomes invalid. Instead, it should be using some sort of allocation (e.g. 'malloc'), or taking in a destination pointer. 2. 'r' is arbitrarily set with length 100. Smaller strings don't need all that space, and larger strings definitely will overrun. 3. The function signature is really awk…

> If the input 't' doesn't have a null terminator Then it's not a string.

Sure, but it is still a valid 'char *' :)

Re: Massacring C Pointers

#29

Yesterday I encountered a similar program on a HN comment chain as shown in this link. I am genuinely confused as to why this program is bad. I am a student and I do not know the best practices regarding pointers, but it is how I would write a program to combine two strings. Can someone please elaborate why it is bad? Are their any good resources to fill gaps in my knowledge? Thanks in advance. Edit: Thank you guys f…

There are multiple problems with that code: 1) return(r) He is returning pointer to temporary! When you declare and initialize variables on stack after that function, they will overwrite memory pointed by r. 2) He is assuming size of string pointed by s(including 0) is less than 100 and also the combined sizes of s and t are less than 100. Stack Overflow! 3) Not an errors, but would not pass my code review: Inconsist…

> Inconsistent Variable declaration

How so?

> I would have used strcpy 2 times

You can't do this, because strcpy doesn't give you the length of the string you copied, which is necessary to put the trailing null byte.

Re: Massacring C Pointers

#30

Yesterday I encountered a similar program on a HN comment chain as shown in this link. I am genuinely confused as to why this program is bad. I am a student and I do not know the best practices regarding pointers, but it is how I would write a program to combine two strings. Can someone please elaborate why it is bad? Are their any good resources to fill gaps in my knowledge? Thanks in advance. Edit: Thank you guys f…

> Can someone please elaborate why it is bad? Are their any good resources to fill gaps in my knowledge?

If you're actually learning C and writing programs in it then the two best things to do would be turn compiler warnings up to 11 and run valgrind often. So you want -Wall and -Wpedantic when you compile and if possible run with valgrind as part of your build script or run it with your tests, just run it often (the longer between runs the harder it is to track back to which change you made).

Post reply on HN