Search code examples
cstringvariablespointersstring-literals

Why does a pointer work and not a normal variable in this code?


In my below code I get the following warnings went built:

In function ‘on_btn_Convert_clicked’:|

warning: assignment makes integer from pointer without a cast [enabled by default]|

warning:passing argument 2 of ‘gtk_label_set_text’ makes pointer from integer without a cast [enabled by default]|

||=== Build finished: 0 error(s), 2 warning(s) (0 minute(s), 1 second(s)) ===|

If i run the program as is it will open the GUI but the button in question will cause a segmentation fault when pressed and crash the program.

#include <stdlib.h>
#include <stdio.h>
//include gtk headers
#include <gtk/gtk.h>

//define pointer variable names
GtkWidget  *plblFileName;
GtkWidget  *pbtnConvert;
GtkWidget  *pbtnFileChooser;


//prototype functions
char on_btn_Convert_clicked();

char on_btn_Convert_clicked()
{
   //define variables
   char hello;

   hello = "hello!";

   gtk_label_set_text(GTK_LABEL(plblFileName), hello);
   return 0;
}

//start main loop
int main( int    argc,
      char **argv )
{
    GtkBuilder *builder;
    GtkWidget  *window;
    GError     *error = NULL;

    //Init GTK+
    gtk_init( &argc, &argv );

    //Create new GtkBuilder object
    builder = gtk_builder_new();
    //Load UI from file. If error occurs, report it and quit application.
    //Replace "tut.glade" with your saved project.
    if( ! gtk_builder_add_from_file( builder, "testGTK.ui", &error ) )
    {
        g_warning( "%s", error->message );
        g_free( error );
        return( 1 );
    }

    //Get main window pointer from UI
    window = GTK_WIDGET( gtk_builder_get_object( builder, "windowMain" ) );

    // get pointer to the label and button
    plblFileName = GTK_WIDGET(gtk_builder_get_object(builder, "lbl_FileName"));
    pbtnConvert = GTK_WIDGET(gtk_builder_get_object(builder, "btn_Convert"));
    pbtnFileChooser = GTK_WIDGET(gtk_builder_get_object(builder, "btn_Choose"));

    //connect the button with its signal
    g_signal_connect(G_OBJECT(pbtnConvert), "clicked", G_CALLBACK(on_btn_Convert_clicked), NULL);

    //Destroy builder, since we don't need it anymore
    g_object_unref( G_OBJECT( builder ) );

    //Show window. All other widgets are automatically shown by GtkBuilder
    gtk_widget_show( window );

    //Start main loop
    gtk_main();

    return( 0 );
}

However, if I make the variable 'hello' a pointer then the errors go away and everything works.

Like this:

char on_btn_Convert_clicked()
{
   //define variables
   char *hello;

   hello = "hello!";

   gtk_label_set_text(GTK_LABEL(plblFileName), hello);
   return 0;
}

I dont understand how a pointer, the object that just points to a place in memory, can be made equal to "hello" and still work like this?

Can anyone explain to me why the 'char hello' variable needs to be a pointer (*) and cant just be a string or 'char[]'?

Finally, can you summarize why this is working versus the version with a normal char variable?


Solution

  • A constant string literal like "hello" is actually an array of six characters ('h', 'e', 'l', 'l', 'o' and the terminator '\0'). When you use it, it can decay to a pointer to its first element.

    It's like having an actual array:

    char hello_array[6] = { 'h', 'e', 'l', 'l', 'o', '\0' };
    

    and then assigning it to a pointer like

    char *hello = &hello_array[0];
    

    The above is what is basically what is happening when you do

    char *hello = "hello";
    

    When you have

    char hello = "hello";
    

    it's like doing

    char hello = &hello_array[0];
    

    That simply doesn't work. It's the same when calling the function, it expects an argument of type char * and you pass an argument of type char. Those two types are not the same by a long stretch.


    Another way of explaining it might be for you to think something like this: A variable is a place to store a value. When you have a variable of type char you can store a single character. When you have a pointer to a character you can store exactly that, a pointer to a character (actually a memory address).

    For example, lets say we have

    char a = 'a';
    char *b = &a;
    

    Somewhat graphically it could be seen like this:

                    +-----+
    The variable a: | 'a' |
                    +-----+
                    ^
                    |
                    +-----------------------+
    The variable b: | Address of variable a |
                    +-----------------------+
    

    The variable b points to variable a.

    The difference should be quite obvious if you print the sizes of the two types:

    printf("sizeof(char) = %zu\n", sizeof(char));
    printf("sizeof(char *) = %zu\n", sizeof(char *));
    

    The first line should say that the size of char is 1 (it's specified in the C specification to always be 1 by the way). The size of the pointer should be either 4 (on a 32-bit system) or 8 (on a 64-bit system).