# const variables

### let's take a sample program like this

```c
void my_memcpy(void dest, const void src, size_t n) 
{ 
    char csrc = (char *)src; 
    char cdest = (char *)dest; 
    for (size_t i = 0; i < n; i++) 
        cdest[i] = csrc[i]; 
    return dest; 
}
```

why are we casting? We are casting the void pointers to unsigned char pointers because void pointers cannot be dereferenced. The unsigned char data type is used because it is guaranteed to have a size of 1 byte and to be able to represent any object as a byte sequence. The cast allows us to perform pointer arithmetic and treat the memory being copied as a sequence of bytes.

but why are we passing a void pointer in the first place? The void pointer dest is used to allow memcpy to copy data into any kind of pointer variable. When we don't know the exact data type of the destination, we can use a void pointer as a generic type. Since a void pointer can point to any data type, we need to cast it to the appropriate pointer type before we can dereference it and copy data into it.

The void pointer is cast to a char pointer before performing the copying operation. This is because the char data type is guaranteed to be 1 byte in size, and so we can safely copy 1 byte at a time using char pointers. If we were copying data of a different data type, we would need to cast the void pointer to the appropriate pointer type for that data type.

### How to assign a value to a constant variable in a struct after the object has been created

using the following code as an example

```c
// creating a new object
listint_t *create_listint(const int *array, size_t size)
{
    listint_t *list;
    listint_t *node;
    int *tmp;

    list = NULL;
    while (size--)
    {
        node = malloc(sizeof(*node));
        if (!node)
            return (NULL);
        tmp = (int *)&node->n;   << emphasis on this line
        *tmp = array[size];      << emphasis on this line
        node->next = list;
        node->prev = NULL;
        list = node;
        if (list->next)
            list->next->prev = list;
    }
    return (list);
}
// main function
int main(void)
{
    listint_t *list;
    int array[] = {19, 48, 99, 71, 13, 52, 96, 73, 86, 7};
    size_t n = sizeof(array) / sizeof(array[0]);

    list = create_listint(array, n);
    print_list(list);
    return (0);
}
//the struct 
typedef struct listint_s
{
    const int n;
    struct listint_s *prev;
    struct listint_s *next;
} listint_t;
```

Let's break down those lines of code marked in the original:

```c
tmp = (int *)&node->n;
*tmp = array[size];
```

In these lines, the code is assigning a value from the `array` to the `n` member of the `node` struct. Here's what each line does:

1. `tmp = (int *)&node->n;` - This line assigns the address of the `n` member of the `node` struct to the `tmp` pointer.
    
    * `&node->n` gets the address of the `n` member.
        
    * `(int *)` casts the address to a pointer to an `int` type and assigns it to `tmp`.
        
    
    The reason for casting the address to an `int*` pointer is that `n` is declared as a `const int` member in the `listint_t` struct. Casting away the `const` qualifier allows the code to modify the value of `n` using the `tmp` pointer.
    
2. `*tmp = array[size];` - This line assigns the value from the `array` to the `n` member of the `node` struct using the `tmp` pointer.
    
    * `*tmp` dereferences the `tmp` pointer, which gives access to the memory location it points to.
        
    * `array[size]` retrieves the value from the `array` at the given index `size`.
        
    * The assignment `*tmp = array[size]` sets the value from the `array` to the memory location that `tmp` points to, which is the `n` member of the `node` struct.
        

### You will get an error when you try to directy change a const variable like node-&gt;n = 1....

If you try to directly change the value of `node->n` without casting it first, it would result in a compilation error.

The `const` qualifier indicates that the `n` member is read-only and cannot be modified. It is a compile-time constraint to ensure that the value of `n` remains constant throughout the program.

If you try to change the value of `node->n` directly without casting away the `const` qualifier, the compiler will generate an error similar to:

```c
error: assignment of read-only member ‘n’
```

This error occurs because the `const` qualifier prevents modifications to the value of `n`.

To work around this issue, the code uses a cast to temporarily remove the `const` qualifier, allowing the assignment to take place.

### How to assign value to a const value in a struct

Since the `n` member of the `listint_t` struct is declared as `const int`, its value cannot be changed once it is initialized. Therefore, to initialize an object of type `listint_t`, you need to provide a value for the `n` member at the time of creation.

**Here's an example of how you can initialize a** `listint_t` **object:**

```c
listint_t node = {42, NULL, NULL};
```

In this example, the `n` member is initialized with the value `42`, and the `prev` and `next` pointers are initialized as `NULL`.

Alternatively, you can initialize the `listint_t` object using designated initializers:

```c
listint_t node = {
    .n = 42,
    .prev = NULL,
    .next = NULL
};
```

This syntax explicitly assigns values to the struct members using designated initializers.

Keep in mind that once the `n` member is assigned a value during initialization, it cannot be modified later due to the `const` qualifier. Therefore, it's crucial to provide the appropriate initial value for `n` during object <mark> creation</mark>.

when a `const` member is not assigned a value during the creation of an object, it does not receive a default value automatically. The behavior is considered uninitialized, and reading the uninitialized `const` member leads to undefined behavior.
