3

We can assign a string in C as follows:

char *string;
string = "Hello";
printf("%s\n", string); // string
printf("%p\n", string); // memory-address

And a number can be done as follows:

int num = 4404;
int *nump = #
printf("%d\n", *nump);
printf("%p\n", nump);

My question then is, why can't we assign a pointer to a number in C just like we do with strings? For example, doing:

int *num;
num = 4404;
// and the rest...

What makes a string fundamentally different than other primitive types? I'm quite new to C so any explanation as to the difference between the two would be very helpful.

Marco Bonelli
  • 63,369
  • 21
  • 118
  • 128
  • 1
    A string in C is stored as an array of characters. – Rishikesh Raje Sep 03 '19 at 04:55
  • Possible duplicate of [Assigning strings to pointer in C Language](https://stackoverflow.com/questions/24690475/assigning-strings-to-pointer-in-c-language) – msc Sep 03 '19 at 04:59
  • Just where is `num = 4404;` in a system using randomized addresses? Now, for some embedded boards with fixed address for LEDs, etc., it does make sense, but those are "freestanding systems", not "hosted systems". – David C. Rankin Sep 03 '19 at 05:13
  • Duplicate: [“Pointer from integer/integer from pointer without a cast” issues](https://stackoverflow.com/questions/52186834/pointer-from-integer-integer-from-pointer-without-a-cast-issues). TL;DR the language simply doesn't allow it, use an explicit cast instead. – Lundin Sep 03 '19 at 11:22
  • Possible duplicate of ["Pointer from integer/integer from pointer without a cast" issues](https://stackoverflow.com/questions/52186834/pointer-from-integer-integer-from-pointer-without-a-cast-issues) – Kamiccolo Sep 03 '19 at 11:34

4 Answers4

5

There is no such type as "string" in C. A string is not a primitive type. A string is just an array of characters, terminated by a NUL byte ('\0').

When you do this:

char *string;
string = "Hello";

What really happens is that the compiler is smart and creates a constant read only char array and then assigns it to your variable string. This can be done because in C the name of an array is the same as the pointer to its first element.

// This is placed in a different section:
const char hidden_arr[] = {'H', 'e', 'l', 'l', 'o', '\0'};

char *string;
string = hidden_arr;
// Same as:
string = &(hidden_arr[0]);

Here, hidden_arr and string are both char *, because as we just said the name of an array is equal to the pointer to its first element. Of course, all of this is done transparently, you will not actually see another variable named hidden_arr, that's just an example. In reality the string will be stored in some location in your executable without a name, and the address of that location will be copied to your string pointer.

When you try to do the same with an integer, it's wrong because int * and int are different types, and you cannot write this (well, you can, but it's meaningless and does not do what you expect it to):

int *ptr;
ptr = 123;

But, you can very well do it with an array of integers:

int arr[] = {1, 2, 3};
int *ptr;
ptr = arr;
// Same as:
ptr = &(arr[0]);
Marco Bonelli
  • 63,369
  • 21
  • 118
  • 128
  • OK explanations, but amiss in detail. A C _string_ is not an _array_, although a _string literal_ is. The _string literal_ `"Hello"` is not read-only as much as it is something that should not be written - result is UB - it might work, might not, might kill the code. `hidden_arr` (the string literal) is not a `char *` as an array is not a pointer and a pointer is not an array. With `int *ptr; ptr = 123;`, code can do that with very limited value conditions - `int *` and `int` as different types is not the limitation. – chux - Reinstate Monica Sep 03 '19 at 06:29
  • @chux didn't say a char array IS a pointer. I specifically said that the name of a char array and a pointer to its first element are the same thing. doing int *x = 123 will warn you that types mismatch, so it should not be done without a cast. The string literal "hello" might not be required by the standard to be RO, but writing to it is UB, so you might as well consider it RO, which actually usually is, since it's put in a RO section by the compiler. – Marco Bonelli Sep 03 '19 at 06:36
  • "we just said the name of an array is equal to the pointer to its first element." mixes up more the array/pointer differences --> Counter example: compare `sizeof hidden_arr` and `sizeof (char *)` - they differ - the name of an array is not _always_ equal to pointer to first element. Warnings are a good indication that something may be amiss, yet maybe not, else the compiler would have error-ed, not simply warned. Cast does quiet the warning. A compliant compiler/platform need not have any "read-only" memory - common enough among embedded devices and simple processors to lack that. – chux - Reinstate Monica Sep 03 '19 at 06:54
  • *"compare sizeof hidden_arr and sizeof (char *) - they differ"* I know that very well. This: "the name of an array is equal to the pointer to its first element" is true. There's no difference between `arr` and `&(arr[0])`. Again, I did not say that an array is a pointer, I know those are completely different things. --- As per the string, granted that, but treating it as RO is the only thing you can legally do, so that's what I wrote. This is a simple answer for a beginner, no need to go into the depths of the C standard. – Marco Bonelli Sep 03 '19 at 07:58
  • *"else the compiler would have error-ed, not simply warned"* eh... many many warnings *will* result in runtime errors or UB. Assigning one type to another without doing proper conversion is a very useful warning that should not be ignored. This is just a case where the warning *could* be safely ignored, but it should definitely not be, and again this is just a simple answer for a beginner. – Marco Bonelli Sep 03 '19 at 08:01
  • Marco Bonelli, @Shared is a lively poster - not a typical beginner. OP does not need [over simplified answers](https://en.wikipedia.org/wiki/Lie-to-children) with are factual wrong as with "it's wrong because int * and int are different types, and you cannot write `int *ptr; ptr = 123;`". Simplified answers are good - IMO, this one edged over in incorrectly various ways. – chux - Reinstate Monica Sep 03 '19 at 08:38
  • @chux well, I get the point. Feel free to edit to my answer if you want, something could be perhaps rephrased better to stay clear and also be 100% correct. – Marco Bonelli Sep 03 '19 at 08:53
1

What makes a string fundamentally different than other primitive types?

A string seems like a primitive type in C because the compiler understands "foo" and generates a null-terminated character array: ['f', 'o', 'o', '\0']. But a C string is still just that: an array of characters.

My question then is, why can't we assign a pointer to a number in C just like we do with strings?

You certainly can assign a pointer to a number, it's just that a number isn't a pointer, whereas the value of an array is the address of the array. If you had an array of int, then that would work just like a string. Compare your code:

char *string;
string = "Hello";
printf("%s\n", string); // string
printf("%p\n", string); // memory-address

to the analogous code for an array of integers:

int numbers[] = {1, 2, 3, 4, 5, 0};
int *nump = numbers;
printf("%d\n", nump[0]); // string
printf("%p\n", nump);    // memory-address

The only real difference is that the compiler has some extra syntax for arrays of characters because they're so common, and printf() similarly has a format specifier just for character arrays for the same reason.

Caleb
  • 124,013
  • 19
  • 183
  • 272
  • thanks for this. Especially helpful is this part to understanding it: `The only real difference is that the compiler has some extra syntax for arrays of characters because they're so common, and printf() similarly has a format specifier just for character arrays for the same reason.` –  Sep 03 '19 at 20:19
1

why can't we assign a pointer to a number in C just like we do with strings?

int *num;
num = 4404;

Code can do that if 4404 is a valid address for an int.

An integer may be converted to any pointer type. Except as previously specified, the result is implementation-defined, might not be correctly aligned, might not point to an entity of the referenced type, and might be a trap representation.
C11dr §6.3.2.3 5

If the address is not properly aligned --> undefined behavior (UB).
If the address is a trap --> undefined behavior (UB).

Attempting to de-reference the pointer is a problem unless it points to a valid int.

printf("%d\n", *num);

With below, "Hello" is a string literal. It exist someplace. The assignment take the address of the string literal and assigns that to string.

char *string;
string = "Hello";

The point is that that address assigned is known to be valid for a char *.

In the num = 4404; is not known to be valid (it likely is not).


What makes a string fundamentally different than other primitive types?

In C, a string is a C library specification, not a C language one. It is definition convenient to explaining various function therein.

A string is a contiguous sequence of characters terminated by and including the first null character §7.1.1 1

Primitive types are part of the C language.

The languages also has string literals like "Hello" in char *string; string = "Hello";. These have some similarity to strings, yet differ.


I recommend searching for "ISO/IEC9899:2017" to find a draft copy of the current C spec. It will answer many of your 10 question of the last week.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
0

The type pf a string literal (e.g. "hello world") is a char[]. Where assigning char *string = "Hello" means that string now points to the start of the array (e.g. the address of the first memory address in the array: &char[0]).

Whereas you can't assign an integer to a pointer because their types are different, one is a int the other is a pointer int *. You could cast it to the correct type:

int *num;
num = (int *) 4404;

But this would be considered quite dangerous (unless you really know what you are doing). I.e. do you know what is a memory adress 4404?

code_fodder
  • 15,263
  • 17
  • 90
  • 167