24

suppose we declare two variable


char s[]= "hello"


char* p = "hello".


what is the difference between these two?

3
  • Technically, the type char [] is different from the type char *. However, an array name can be used as a pointer to the array. In your example, we can probably say that there is no difference.
    – kzidane
    Commented Feb 21, 2015 at 17:58
  • can you pls explain what's the technical difference @Kareem Commented Feb 24, 2015 at 12:55
  • 1
    A variable of type char [] is always a char array while a variable of type char * could be a pointer to a single char or a char array.
    – kzidane
    Commented Feb 24, 2015 at 13:26

2 Answers 2

50

The difference between char* the pointer and char[] the array is how you interact with them after you create them.

If you are just printing the two examples, it will perform exactly the same. They both generate data in memory, {h, e, l, l, o, /0}.

The fundamental difference is that in one char* you are assigning it to a pointer, which is a variable. In char[] you are assigning it to an array which is not a variable.

char[] is a structure, it is specific section of memory, it allows for things like indexing, but it always will start at the address that currently holds 'h'.

char* is a variable. It was initialized with a number, but we can change this number using mathematical operators such as ++, because it is essentially an integer.

So here's one example, where the pointer would be much more efficient than an array. Say, for whatever reason we wanted the string to say "ello" instead of "hello". With a pointer all we need to do is to shift the pointer one position to the "right".

char* p = "hello";
p++;

This is a very fast operation and runs in Big O of 1 (literally, in this case it is a single very fast operation)

But with char[], we can't change where the array starts, therefore we actually need to do something much less efficient. We need to loop through the entire word and for every index change the char in memory. It would look something like this. (manually done instead of looping for clarity)

char s[] = "hello";
s[0] = 'e';
s[1] = 'l';
s[2] = 'l';
s[3] = 'o';
s[4] = '/0';

This is a far slower operation running in "big O of n".

5
  • 1
    I have look into so many places and this is by far the best explanation I have seen. thanks!
    – ananvodo
    Commented Mar 1, 2019 at 23:30
  • @AntoineWood so you mean to say s in char s[]= "hello" is not just any variable, it's a structure variable? Commented Jun 4, 2019 at 12:02
  • 1
    was looking for a proper explanation with examples. This is the best i've seen so far. Thank you! Commented Jul 21, 2019 at 2:49
  • If you have a function int getline(char buffer[]) vs int getline(char *buffer) in which you read the chars from the console one by one, adding them to the buffer variable which is declared in main as char inputLine[50], which would be the difference? I have tried replace one prototype with the other and it does not change my program's functionality. I can still acces elements with the index buffer[idx] in either case! Commented Oct 27, 2019 at 20:06
  • @DragosMakovei in the case of function parameters, char buffer[] and char *buffer are equivalent. According to section 5.3 of The C Programming Language by K&R, "we prefer the latter because it says more explicitly that the variable is a pointer."
    – David D.
    Commented Jun 17, 2023 at 16:06
8

C99 N1256 draft

There are two different uses of character string literals:

  1. Initialize char[]:

    char c[] = "abc";      
    

    This is "more magic", and described at 6.7.8/14 "Initialization":

    An array of character type may be initialized by a character string literal, optionally enclosed in braces. Successive characters of the character string literal (including the terminating null character if there is room or if the array is of unknown size) initialize the elements of the array.

    So this is just a shortcut for:

    char c[] = {'a', 'b', 'c', '\0'};
    

    Like any other regular array, c can be modified.

  2. Everywhere else: it generates an:

    So when you write:

    char *c = "abc";
    

    This is similar to:

    /* __unnamed is magic because modifying it gives UB. */
    static char __unnamed[] = "abc";
    char *c = __unnamed;
    

    Note the implicit cast from char[] to char *, which is always legal.

    Then if you modify c[0], you also modify __unnamed, which is UB.

    This is documented at 6.4.5 "String literals":

    5 In translation phase 7, a byte or code of value zero is appended to each multibyte character sequence that results from a string literal or literals. The multibyte character sequence is then used to initialize an array of static storage duration and length just sufficient to contain the sequence. For character string literals, the array elements have type char, and are initialized with the individual bytes of the multibyte character sequence [...]

    6 It is unspecified whether these arrays are distinct provided their elements have the appropriate values. If the program attempts to modify such an array, the behavior is undefined.

6.7.8/32 "Initialization" gives a direct example:

EXAMPLE 8: The declaration

char s[] = "abc", t[3] = "abc";

defines "plain" char array objects s and t whose elements are initialized with character string literals.

This declaration is identical to

char s[] = { 'a', 'b', 'c', '\0' },
t[] = { 'a', 'b', 'c' };

The contents of the arrays are modifiable. On the other hand, the declaration

char *p = "abc";

defines p with type "pointer to char" and initializes it to point to an object with type "array of char" with length 4 whose elements are initialized with a character string literal. If an attempt is made to use p to modify the contents of the array, the behavior is undefined.

GCC 4.8 x86-64 ELF implementation

Program:

#include <stdio.h>

int main(void) {
    char *s = "abc";
    printf("%s\n", s);
    return 0;
}

Compile and decompile:

gcc -ggdb -std=c99 -c main.c
objdump -Sr main.o

Output contains:

 char *s = "abc";
8:  48 c7 45 f8 00 00 00    movq   $0x0,-0x8(%rbp)
f:  00 
        c: R_X86_64_32S .rodata

Conclusion: GCC stores char* it in .rodata section, not in .text.

If we do the same for char[]:

 char s[] = "abc";

we obtain:

17:   c7 45 f0 61 62 63 00    movl   $0x636261,-0x10(%rbp)

so it gets stored in the stack (relative to %rbp).

Note however that the default linker script puts .rodata and .text in the same segment, which has execute but no write permission. This can be observed with:

readelf -l a.out

which contains:

 Section to Segment mapping:
  Segment Sections...
   02     .text .rodata
3
  • 1
    Thank you. I created an account just to upvote this answer. Commented Nov 30, 2020 at 4:52
  • @ChanderSiddarth thanks!! Commented Nov 30, 2020 at 9:47
  • 1
    this is great! thank you!
    – ZIFARIN
    Commented Oct 18, 2021 at 19:12

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .