1

According to this answer, constants shouldn't be deleted because they may not be allocated in the first place. However, there exists some cases where I want to protect dynamically allocated data. For example, when managing user sessions, I want to make sure that data like the current user name won't accidentally get overwritten by other functions, but the session data are indeed allocated when they are parsed from the request.

Here is a code example to give you a better idea. Please keep in mind that things are greatly simplified, though.

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


const char *request = "www.example.com/index?first-name=SOME&last-name=DUDE";

char *get_username_from_request(const char *req) {
    char *first_name = strstr(req, "first-name") + strlen("first-name") + 1;
    char *last_name = strstr(req, "last-name") + strlen("last-name") + 1;

    char *username = malloc(10);
    memcpy(username, first_name, 4);
    username[4] = ' ';
    memcpy(username + 5, last_name, 4);

    /* The returned string is "SOME DUDE" */
    return username;
}

int main(void) {
    char *username = get_username_from_request(request);
    printf("Welcome, %s.\n", username);
    free(username);
    return 0;
}

I really want to use const char *username = get_username_from_request(request); instead, but Clang emits a warning when I make this change:

/Applications/CLion.app/Contents/bin/cmake/bin/cmake --build /Users/nalzok/CLionProjects/zero/cmake-build-debug --target zero -- -j 2
Scanning dependencies of target zero
[ 50%] Building C object CMakeFiles/zero.dir/main.c.o
/Users/nalzok/CLionProjects/zero/main.c:24:10: warning: passing 'const char *' to parameter of type 'void *' discards qualifiers [-Wincompatible-pointer-types-discards-qualifiers]
    free(username);
         ^~~~~~~~
/usr/include/stdlib.h:151:18: note: passing argument to parameter here
void     free(void *);
                    ^
1 warning generated.
[100%] Linking C executable zero
[100%] Built target zero

Any idea is appreciated.

†: Well, believe it or not, I'm writing a Web application in C.

nalzok
  • 14,965
  • 21
  • 72
  • 139
  • Possible duplicate of [Unable to free const pointers in C](https://stackoverflow.com/questions/2819535/unable-to-free-const-pointers-in-c) – Sander De Dycker Jul 25 '18 at 08:02
  • @SanderDeDycker Yeah suppressing the warning by casting is the obvious answer and it works. However, you have to be very careful when doing this, so I want to see if someone can provide a safer alternative. For example, maybe there is a way to "lock" some memory, preventing subsequent writing operations. – nalzok Jul 25 '18 at 08:09
  • 2
    the accepted (and most voted) answer in the duplicate question isn't the only answer - it's just the one that the community supports most. Other answers recommend not using `const` in this case eg. You could do `char* u = malloc(...); const char* cu = u; puts(cu); free(u);` - ie. use `u` only for the de-allocation, and `cu` for everything else. – Sander De Dycker Jul 25 '18 at 08:19
  • 1
    `The returned string is "SOME DUDE"` is not completely correct. `username` does not certainly point to a _string_ as the data pointed to by `username` does not certainly contain a _null character_. – chux - Reinstate Monica Jul 25 '18 at 11:42
  • @chux My bad, forgot to add a NUL terminator. Good catch. – nalzok Jul 25 '18 at 13:22

1 Answers1

2

First of all, you must understand that there's nothing ever preventing your compiled code from overwriting any data, be it declared const or not. Still const correctness helps you spotting logical errors in your code, so it's a good idea.

For your concrete problem, I would suggest doing a little OOP-like information hiding here. You could e.g. model a user session like this:

session.h:

#ifndef SESSION_H
#define SESSION_H

typedef struct Session Session;

Session *Session_create(void);
int Session_setUsername(Session *self, const char *username);
const char *Session_Username(const Session *self);
void Session_destroy(Session *self);

#endif

session.c:

#include <stdlib.h>
#include <string.h>
#include "session.h"

struct Session
{
    char *username;
};

Session *Session_create(void)
{
    Session *self = calloc(1, sizeof *self);
    return self;
}

int Session_setUsername(Session *self, const char *username)
{
    free(self->username);
    self->username = malloc(strlen(username) + 1);
    if (!self->username) return -1;
    strcpy(self->username, username);
    return 0;
}

const char *Session_Username(const Session *self)
{
    return self->username;
}

void Session_destroy(Session *self)
{
    if (!self) return;
    free(self->username);
    free(self);
}

Now, if you have somewhere in your code a const Session *, you won't be able to manipulate any session data. You can only get a const pointer to your username.