-1

I have written the following code in C. I need to understand how the string copy operations will be performed after the character pointer gets assigned memory via malloc() dynamically.

My code:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define BUFFSZ 20
int main()
{
        char *name = NULL;
        char my_name[BUFFSZ] ;
        memset(my_name,0,BUFFSZ);
        strcpy(my_name,  "vinothkumarsaradavallivaradathirupathi");
        printf("string copied is %s\n",my_name);
        if ((name = malloc(1 + strlen(my_name)+1)) != NULL)
                strcpy(name,my_name);
        printf("Name is %s\n",name);
        free(name);
        name = NULL;
        return 0;
}

Actual output:

string copied is vinothkumarsaradavallivaradathirupathi
Name is vinothkumarsaradavalliva��

According to the code, I have expected the output below but got only one above. It will be helpful if someone explains this clearly.

Expected output:

string copied is vinothkumarsaradaval
Name is vinothkumarsaradaval

When I ran this code in GDB, I got the following output:

Breakpoint 2, main () at first_pgm.c:12
12              memset(my_name,0,BUFFSZ);
(gdb) n
14              strcpy(my_name,  "vinothkumarsaradavallivaradathirupathi");
(gdb) p name
$1 = 0x0
(gdb) p my_name
$2 = '\000' <repeats 19 times>
(gdb) n

Breakpoint 3, main () at first_pgm.c:15
15              printf("string copied is %s\n",my_name);
(gdb) p my_name
$3 = "vinothkumarsaradaval"
(gdb) n
string copied is vinothkumarsaradavallivaradathirupathi

Here, why "$3" and "string copied" outputs are conflicting?

Iharob Al Asimi
  • 52,653
  • 6
  • 59
  • 97

3 Answers3

2

Expected Output

Wait. You cannot have an expected output out of this code. This code produces undefined behavior.

In your code

 strcpy(my_name,  "vinothkumarsaradavallivaradathirupathi")

you're overrunning the allocated memory. In your case my_name does not have enough memory to hold the complete content of the source string literal.

Result: undefined behavior.

Please allocate enough memory to the destination buffer so that it can hold the source string and the null-terminator.

That said,

  1. Do not cast the return value of malloc() and family.

  2. The recommended (rather, required) signature of main() is int main(void) when you're not intending to use any command line arguments.

Community
  • 1
  • 1
Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
  • It's actually the [_required_](http://port70.net/~nsz/c/c11/n1570.html#5.1.2.2.1p1) signature, not just recommended. – too honest for this site Jun 25 '15 at 20:11
  • @Olaf I used _"recommended"_ to leave space for _"implementation-defined manner"_ part. Hope i'm not misunderstood. :-) – Sourav Ghosh Jun 25 '15 at 20:12
  • Well, for hosted environments, there is actually no space left. And for freestanding, there is no recommendation at all. So: I appreciate you being cautious with absolute statements, but this time: just do it, be brave! ;-)) – too honest for this site Jun 25 '15 at 20:15
  • Ok, I got you now. I interpret that still the the basis has to be `int main(...)`. So the return type is not open to change, but it is allowed to use an alias to `int`. The "implementation-defined manner" applies to the arguments ony (imo mostly to how they are constructed by the startup code which actually calls `main()`). – too honest for this site Jun 25 '15 at 20:22
  • @Olaf :-). OK, i'll take your leave for today. It's 2:00 AM here ([IST](https://en.wikipedia.org/wiki/Indian_Standard_Time)). See you all tomorrow. :-) – Sourav Ghosh Jun 25 '15 at 20:26
  • @Olaf It's not required; there may be arguments to main. And it is debatable whether `()` is an acceptable replacement for `(void)`. – M.M Jun 25 '15 at 22:02
  • @MattMcNabb: Sorry I was a bit unclear. I mostly refer to the `int` return type, not to the arguments. You are right, of course: there are two variants defined by the standard: `void` (no arguments) or `int argc, char *argv[]`. The latter with certain semantics.. Regarding using `void` for functions taking no arguments: That's how I learned it (and do it). However, I am not 100% sure if that is still required by the standard, so I do not criticise if it is missing. If you can point me to the standard here, would be great. – too honest for this site Jun 25 '15 at 22:09
  • @Olaf http://stackoverflow.com/questions/29190986/is-int-main-without-void-valid-and-portable-in-iso-c – M.M Jun 25 '15 at 22:14
  • @MattMcNabb: Thanks, but as a quote, of to me unknown origin, stated: "After finding answers to all our questions, we do not feel less confused. But we now feel confused at a higher level." IOW: The comitee said no, but in the C11 draft at least they still use `()`. Well, at least sticking to `(void)` _does_ conform. I'll stick with this. – too honest for this site Jun 25 '15 at 22:30
1

You are copying 19 bytes more than allowed, that causes undefined behavior. Because your array can only store 20 characters but you are copying 39 to it.

Just change

#define BUFSIZE 39

and it will work.

Also, this

if (NULL != (name = (char *)malloc(sizeof(char)*(strlen(my_name)+1))))

is extreamly ugly,

  1. Don't cast the return value of malloc() which is void * and it is converted to any other pointer type without casting.
  2. Don't use sizeof(char) because it's 1 by definition.

Fixing the code it would look like

if ((name = malloc(1 + strlen(my_name))) != NULL)

And you check for NULL but you still do this

printf("Name is %s\n", name)

this will cause undedfined behavior in case name == NULL.

Iharob Al Asimi
  • 52,653
  • 6
  • 59
  • 97
  • useful one, as always, +1. :-) – Sourav Ghosh Jun 25 '15 at 20:18
  • Points #2 and #3 have solid technical justifications, but Yoda conditions are a matter of opinion. I don't like them, but stating an opinion in these absolute terms may make people less likely to heed the rest of your advice (which is good, btw). – trent Jun 25 '15 at 20:22
  • Removed yoda conditions and changed the code as per ur guidelines. – renga_in_stack Jun 25 '15 at 21:17
  • The `!= NULL` is redundant anyway – M.M Jun 25 '15 at 22:01
  • @MattMcNabb You're right, it continues to `printf()` it. – Iharob Al Asimi Jun 25 '15 at 22:03
  • @iharob I mean , `if (x)` is equivalent to `if (x != NULL)` when `x` is a pointer. I'm taking some objection to you giving style advice but presented as if the original code was actually incorrect, and imposing your own style instead. – M.M Jun 25 '15 at 22:04
  • @MattMcNabb I see, I still don't like it because when I see `!= NULL` I immedately know that it's a pointer. But it's perfectly good and normally not a problem, there are some languages where actually `if (x)` is the way to do it, because there are many possible values that would evaluate to false, so comparing with each one is tedious. Oh, and I noticed the `printf()` and added it to the answer, it's also good to avoid that anyway. – Iharob Al Asimi Jun 25 '15 at 22:06
0

This is a classic buffer overflow. strcpy will not stop writing characters into its destination buffer until it finds a terminating '\0' character. The memory into which it is writing is implementation-defined, which is why you can't reliably read it back out. Use strncpy to put a ceiling on the amount of data strcpy will copy.

Derek T. Jones
  • 1,800
  • 10
  • 18
  • Yes , I got your point. But when i run GDB, I got something like below – renga_in_stack Jun 25 '15 at 20:22
  • Breakpoint 2, main () at first_pgm.c:12 12 memset(my_name,0,BUFFSZ); (gdb) n 14 strcpy(my_name, "vinothkumarsaradavallivaradathirupathi"); (gdb) p name $1 = 0x0 (gdb) p my_name $2 = '\000' (gdb) n Breakpoint 3, main () at first_pgm.c:15 15 printf("string copied is %s\n",my_name); (gdb) p my_name $3 = "vinothkumarsaradaval" – renga_in_stack Jun 25 '15 at 20:22
  • The debugger, knowing that only 20 characters are part of the variable my_name, will only display that many, regardless of how many strcpy wrote. – Derek T. Jones Jun 25 '15 at 22:22