4

I have just started to learn how to program and I am using this book called Head First C. There are sections in the book called Brain Power. In one of these sections it was written.

void fortune_cookie(char msg[])
{
  printf("Message reads: %s\n", msg);
  printf("msg occupies %i bytes\n", sizeof(msg));
}

The output:

Cookies make you fat

msg occupies 8 bytes

The Brain Power question was: Why do you think sizeof(msg) is shorter than the length of the whole string? What is msg? Why would it return different sizes on different machines?

Community
  • 1
  • 1
Maisha
  • 41
  • 1
  • Possible duplicate of [How to find the 'sizeof'(a pointer pointing to an array)?](http://stackoverflow.com/questions/492384/how-to-find-the-sizeofa-pointer-pointing-to-an-array) – BlackDwarf Nov 19 '15 at 10:17

4 Answers4

4

In this particular case

 char msg[]

is same as

 char * msg

so what you're really seeing is the output of sizeof(char *).

As the size of a pointer is dependent on the architecture/compiler, you'll see different output in different machine.

Also, please note, as the sizeof operator produces a result of type size_t, you should be using %zu format specifier to print the result.

Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
3

Why do you think sizeof(msg) is shorter than the length of the whole string?

Because arrays decay to pointers when passed as parameters to some function.

int arr[10];
printf("%zu", sizeof(arr)); 

This would return 40. Considering size of int is 4

But when sent to a function

void func(int arr[])
{
    printf("%lu", sizeof(arr));
}

This would return sizeof(int*)


What is msg?

msg is an array of char that is sent to the function.


Why would it return differentsizes on different machines?

Because the size of the memory address would be different for different machines architectures, thus making sizeof(int*) different.

Haris
  • 12,120
  • 6
  • 43
  • 70
2

The following piece of code should Answer to your Question:

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

void foo(char *msg){
    printf("\n");
    printf("Sizeof MSG = %zu\n", sizeof(msg));
    printf("Length of MSG = %zu\n", strlen(msg));
}

int main(void) {

    char msg[10] = "Michi";
    printf("Sizeof MSG = %zu\n", sizeof(msg));
    printf("Length of MSG = %zu\n", strlen(msg));

    foo(msg);
    return 0;
}

Output:

Sizeof MSG = 10
Length of MSG = 5

Sizeof MSG = 8
Length of MSG = 5

Why is Sizeof MSG = 10 inside main ? Because you print the size of the Array.

Why is Sizeof MSG = 8 inside foo ? Because you print the size of the Pointer, which on your machine (like mine) happens to be 8.

Arrays decays to pointer to its first element, when are used as Function arguments.

In other words, things like this:

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

void foo(int *msg){
    printf("\n");
    printf("Sizeof MSG = %zu\n", sizeof(msg));
    printf("Length of MSG = %zu\n", strlen(msg));
}

int main(void) {

    int msg[10] = {1,2,3,4,5};
    printf("Sizeof MSG = %zu\n", sizeof(msg));
    printf("Length of MSG = %zu\n", strlen(msg));

    foo(msg);
    return 0;
}

Will not work and probably your compiler will warn you about that:

error: passing argument 1 of ‘strlen’ from incompatible pointer type

Because strlen is defined like this:

size_t strlen(const char *str)

As you can see strlen need a char* and not an int*.

To fix it you need to pass the length too, like this:

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

void foo(int *msg, size_t length){
    size_t i=0;

    printf("\n\n");
    printf("Sizeof MSG = %zu\n",length);
    for (i = 0; i<length;i++){
        printf("%d ",msg[i]);
    }
}

int main(void) {
    int msg[] = {1,2,3,4,5,6,7,8,9,10};
    size_t length = sizeof msg / sizeof msg[0];
    size_t i=0;

    printf("\n");
    printf("Sizeof MSG = %zu\n",length);
    for (i = 0; i<length;i++){
        printf("%d ",msg[i]);
    }


    foo(msg, length);
    return 0;
}

Output:

Sizeof MSG = 10
1 2 3 4 5 6 7 8 9 10 

Sizeof MSG = 10
1 2 3 4 5 6 7 8 9 10
Michi
  • 5,175
  • 7
  • 33
  • 58
0

In C strings and arrays are not first-class data types so cannot be passed-by-copy. Although the argument type syntax char msh[] is supported, it has identical semantics to char* msg. The array argument syntax does little more than to serve to indicate to the human developer that the function expects msg to refer to an array rather then a pointer to a single item.

Where msg is a nul terminated string, or an array with a sentinel value, there may be no need to pass the array length, but in many cases it is useful to do so.

A trick you can use is to wrap the array in a struct which is a first class type and can be passed by copy or pointer. Passing by pointer is usually preferable for performance reasons, but either way the size of the member is known.

typedef struct { char data[32] ; } sMsgContainer ;

void fortune_cookie( const sMsgContainer msg )
{
  printf( "Message reads: %s\n", msg.data ) ;
  printf( "msg occupies %i bytes\n", sizeof(msg) ) ;
  printf( "msg.data occupies %i bytes\n", sizeof(msg.data) ) ;
}


void fortune_cookie2( sMsgContainer* msgp )
{
  printf( "Message reads: %s\n", msgp.data ) ;
  printf( "msgp occupies %i bytes\n", sizeof(msgp) ) ;
  printf( "msgp->data occupies %i bytes\n", sizeof(msg->data) ) ;
}

Then given:

sMsgContainer msg = {"Cookies make you happy"} ;
fortune_cookie( msg ) ;
fortune_cookie2( &msg ) ;

The output

Message reads: Cookies make you happy
msg occupies 32 bytes
msg.data occupies 32 bytes
Message reads: Cookies make you happy
msgp occupies 4 bytes
msgp->data occupies 32 bytes

It is possible for the length of msg to be greater than msg.data for alignment purposes, but that is implementation defined.

Clifford
  • 88,407
  • 13
  • 85
  • 165