-1

First post here, I've used this site for years but this dilemma is really annoying. So when I run this C code through the VS2015 Developer Command prompt with the C compiler:

#include <stdio.h>
#include <string.h>

int main(){
   char* ptr = NULL;
   const char* pt2 = "hello";
   strcpy(&ptr, pt2);
   printf("%s",&ptr);
   return 0;
}

I get these warnings:

midterm_c.c(35): warning C4047: 'function': 'char *' differs in levels of indirection from 'char **'
midterm_c.c(35): warning C4024: 'strcpy': different types for formal and actual parameter 1
midterm_c.c(36): warning C4477: 'printf' : format string '%s' requires an argument of type 'char *', but variadic argument 1 has type 'char **'

Yet when I run it, it prints "hello", which shouldn't happen because it doesn't make sense to use the reference operators on the ptr variable. But when I run this code without them:

char* ptr = NULL;
const char* ptr2 = "hello";
strcpy(ptr, ptr2);
printf("%s",ptr);
return 0;

I get no warnings, successfully compiles, yet when I run it the program crashes even though this should work properly. I'm getting really frustrated because this doesn't make any sense to me and one of the reasons why I'm really hating C right now. If anyone has any idea how this is happening I'd really appreciate it. Thank you.

4 Answers4

3

In your working code, you basically just treated part of your stack as a string buffer; it's flagrantly illegal by the standard, but it happens to work because you don't read from anything you stomp on after the stomping.

You're correct that passing a char** to strcpy makes no sense, but what ends up happening is that it writes to the char* variable itself (and some neighboring memory if it's a 32 bit program), treating it as an array of char (after all, an eight byte pointer has enough room for a seven character string plus NUL terminator). Effectively, your code is incorrect code that still happens to behave like correct code, since this:

char* ptr = NULL;
const char* pt2 = "hello";
strcpy(&ptr, pt2);
printf("%s",&ptr);

compiles to code that behaves almost exactly like this (aside from the warnings):

char ptr[sizeof(char*)];  // Your char* behaves like an array
const char* pt2 = "hello";
strcpy(ptr, pt2);
printf("%s", ptr);

When you don't take the address of ptr, you pass a NULL pointer to strcpy, which crashes immediately (because you can't write to a NULL pointer's target on almost all systems).

Of course, the correct solution would be to make ptr point to allocated memory, either global, automatic, or dynamically allocated memory. For example, using a stack buffer, this would be legal:

char buf[8];

char *ptr = buf; // = &buf[0]; would be equivalent
const char* pt2 = "hello";
strcpy(ptr, pt2);
printf("%s", ptr);

although it would be rather pointless, since ptr could just be replaced with buf everywhere and ptr removed:

char buf[8];
const char* pt2 = "hello";
strcpy(buf, pt2);
printf("%s", buf);
ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
1

First of all, you don't know how to use strcpy. The first parameter needs to point to space available to copy into, it doesn't allocate space for you.

Your first example probably only "works" because it is copying into stack-space. This is undefined behavior and will cause all kinds of problems.

Your second example (the more correct one) will generally cause a segfault because you are trying to copy into (or read from) a null address.

kmdreko
  • 42,554
  • 6
  • 57
  • 106
0

You need to allocate memory for ptr variable (malloc). Then, a usefull function is strdup().
It take a string, allocate the good memory's space for a new string and copy the content of the source in it before returning the new string.
You can do:

ptr = strdup(pt2);

The documentation: http://manpagesfr.free.fr/man/man3/strdup.3.html

YaatSuka
  • 231
  • 1
  • 9
0

You're writing to random memory, which is undefined behavior. This means that according to the C standard, the compiler is allowed to generate code that does literally anything, including but not limited to:

  1. Printing "hello".

  2. Printing random gibberish.

  3. Crashing.

  4. Corrupting some other variable in your app.

  5. Ripping a hole in the fabric of space-time, causing an invasion by evil space lemurs from the fifth dimension that ruin the market for Kopi Luwak coffee by causing a glut in the supply.

Tl;dr: Don't do that.

Charles Srstka
  • 16,665
  • 3
  • 34
  • 60
  • Actually, `&ptr` is not "random" memory. The OP is writing to the bytes of the pointer itself. It's terrible, and probably officially undefined behavior, but it's not "random" memory; it's a known location on the stack. – ShadowRanger Sep 28 '17 at 00:11
  • @ShadowRanger "hello" is longer than 4 bytes, so on a 32-bit operating system this will overrun the bounds of the pointer. On a 64-bit machine, "hello" would fit within the pointer, but if it's longer than 7 characters, as the original code that the OP simplified to this example probably was, it'll overrun that too and write over who knows what. Either way it's bad juju. – Charles Srstka Sep 28 '17 at 00:15