tags:

views:

524

answers:

11

Hi

My structure looks as follows:

typedef struct {
      unsigned long attr;
      char fileName[128];
} entity;

Then I try to assign some values but get an error message...

int attribute = 100;
char* fileNameDir = "blabla....etc";

entity* aEntity;

aEntity->attr = attributes;
aEntity->fileName = fileNameDir;

Compiler tells me:

Error: #137: expression must be a modifiable lvalue aEntity->fileName = fileNameDir;

Why cant I assign here this character to the one in the structure?

Thanks

+4  A: 

You should be copying the filename string, not changing where it points to.

Alex
use strcpy(aEntity->fileName, fileNameDir )
Dewfy
Uff yes, that makes absolute sense. isnt it better to use memcpy which should be safer what I read once? Like memcpy(aEntity->fileName, fileNameDir, FILE_NAME_LENGTH);
@Claus, no, that would potentially read from fileNameDir past its allocated length. Reading past the end of allocation invokes the demons of Undefined Behavior just as badly as writing past the end of an allocation. In this case `strncpy()` would be better than `strcpy` because it can be limited to the size of the destination. Just beware because it might not actually nul terminate the result.
RBerteig
@Claus: memcpy() will copy exactly FILE_NAME_LENGTH characters. If the filename is actually shorter this may cause problems if the source buffer is smaller. For the sake of safety use strncpy() - it will copy no more that the specified amount AND no more characters than actually present in the null-terminated source.
sharptooth
A: 

You're trying to use char* as if it was a string, which it is not. In particular, you're telling the compiler to set filename, a 128-sized char array, to the memory address pointed by fileNameDir.

Use strcpy: http://cplusplus.com/reference/clibrary/cstring/strcpy/

suszterpatt
...and risk a buffer overflow.
sbi
It's hard not to risk a buffer overflow when one uses char arrays for string manipulation. I operate under the assumption that there is some underlying reason for not using std::string, which would of course be otherwise preferrable.
suszterpatt
@suszterpatt: There's `strncpy`. See some of the other answers.
sbi
+1  A: 

You can't assign a pointer to char to a char array, they're not compatible that way, you need to copy contents from one to another, strcpy, strncpy...

Arkaitz Jimenez
+11  A: 

You can't assign a pointer to an array. Use strncpy() for copying the string:

strncpy( aEntity->fileName, fileNameDir, 128 );

This will leave the destination not null-terminated if the source is longer than 128. I think the best solution is to have a bigger-by-one buffer, copy only N bytes and set the N+1th byte to zero:

#define BufferLength 128

typedef struct {
  unsigned long attr;
  char fileName[BufferLength + 1];
} entity;

strncpy( aEntity->FileName, fileNameDir, BufferLength );
*( aEntity->FileName + BufferLength ) = 0;
sharptooth
Do be aware that if `strlen(fileNameDir) >= 128` then `aEntity->fileName` won't end up nul terminated. In absence of a better choice of standard library copy function, I usually explicitly set the last byte of the destination to 0 to avoid future confusion after *every* call to `strncpy()`.
RBerteig
Yes, such problem indeed exists. I guess it would be better to have a buffer of N+1 size, do strncpy(,,N) and set the N+1th byte to zero each time.
sharptooth
magic numbers are bad, IMHO
Casey
@Casey: Suppose you need to allocate a buffer for string concatenation. Do you really think that 1 for the trailing zero is a magic number?
sharptooth
@sharptoot A better alternative is to use `strncat` http://stackoverflow.com/questions/1265117/structure-problem-in-c-c/1265892#1265892
Sinan Ünür
@sharptooth: I read Casey's comment as referring to the 128.
sbi
A: 

For char fileName[128], fileName is the array which is 128 char long. you canot change the fileName. You can change the content of the memory that filename is pointing by using strncpy( aEntity->fileName, fileNameDir, 128 );

Learner
Actually, `filename` is not a pointer, but an array.
sbi
+1  A: 

Use strncpy():

strncpy( aEntity->fileName, fileNameDir, sizeof(entity.fileName) );
aEntity.fileName[ sizeof(entity.fileName) - 1 ] = 0;

The strncpy() function is similar, except that not more than n bytes of src are copied. Thus, if there is no null byte among the first n bytes of src, the result will not be null-terminated. See man page.

Casey
+9  A: 
  • You're treating a char[] (and a char*, FTM) as if it was a string. Which is is not. You can't assign to an array, you'll have to copy the values. Also, the length of 128 for file names seems arbitrary and might be a potential source for buffer overflows. What's wrong with using std::string? That gets your rid of all these problems.
  • You're defining a pointer to some entity, don't initialize it, and then use it as if at the random address it points to was a valid entity object.
  • There's no need to typedef a struct in C++, as, unlike to C, in C++ struct names live in the same name space as other names.

If you absolutely must use the struct as it is defined in your question (it is pre-defined), then look at the other answers and get yourself "The C Programming Language". Otherwise, you might want to use this code:

struct entity {
  unsigned long attr;
  std::string fileName;
};

entity aEntity;
aEntity.attr = 100;
aEntity.filename = "blabla....etc";
sbi
finally - some advise on using a standard string! Thank you!
xtofl
+1  A: 

1) The line char* fileNameDir = "blabla....etc" creates a pointer to char and assigns the pointer an address; the address in this case being the address of the text "blabla....etc" residing in memory.

2) Furthermore, arrays (char fileName[128]) cannot be assigned to at all; you can only assign to members of an array (e.g. array[0] = blah).

Knowing (1) and (2) above, it should be obvious that assigning an address to an array is not a valid thing to do for several reasons.

What you must do instead is to copy the data that fileNameDir points to, to the array (i.e. the members of the array), using for example strncpy.

Also note that you have merely allocated a pointer to your struct, but no memory to hold the struct data itself!

olovb
+4  A: 

Are you writing C or C++? There is no language called C/C++ and the answer to your question differs depending on the language you are using. If you are using C++, you should use std::string rather than plain old C strings.

There is a major problem in your code which I did not see other posters address:

entity* aEntity;

declares aEntity (should be anEntity) as a pointer to an entity but it is not initialized. Therefore, like all uninitialized pointers, it points to garbage. Hence:

aEntity->attr = attributes;

invokes undefined behavior.

Now, given a properly initialized anEntity, anEntity->fileName is an array, not a pointer to a character array (see question 6.2 in the C FAQ list). As such, you need to copy over the character string pointed to by fileNameDir to the memory block reserved for anEntity->fileName.

I see a lot of recommendations to use strncpy. I am not a proponent of thinking of strncpy as a safer replacement for strcpy because it really isn't. See also Why is strncpy insecure?

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

typedef struct st_entity {
    unsigned long attr;
    char fileName[FILENAME_MAX + 1];
} entity;

int main(void) {
    int status = EXIT_FAILURE;
    unsigned long attribute = 100;
    char *fileNameDir = "blabla....etc";

    entity *anEntity = malloc(sizeof(*anEntity));

    if ( anEntity ) {
        anEntity->attr = attribute;
        anEntity->fileName[0] = '\0';
        strncat(anEntity->fileName, fileNameDir, sizeof(anEntity->fileName) - 1);
        printf("%lu\n%s\n", anEntity->attr, anEntity->fileName);
        status = EXIT_SUCCESS;
    }
    else {
        fputs("Memory allocation failed", stderr);
    }

    return status;
}

See strncat.

Sinan Ünür
Too bad I can only give you one vote, as your rise several very good points. :)
sbi
@sbi Thanks. I have felt the same way about others' posts many times.
Sinan Ünür
Good answer, but the last parameter in the call to `strncat()` should be either `sizeof(anEntity->fileName) - 1` or `FILENAME_MAX`.
caf
@caf I originally had `FILENAME_MAX`. Then I decided it was not a good idea to have `FILENAME_MAX` all over the place and I forgot to subtract 1. Anyway, thanks for the heads up. Fixed now.
Sinan Ünür
Compared to strncpy, its not much different, maybe slightly safer. All in all you still need to write the null. Ideally I would create a STRNCPY macro that would handle the tailing NULL; then you don't have to worry about an overrun.
Casey
I don't like that `strncpy` pads the target on occasion ;-)
Sinan Ünür
+1  A: 

First of all, is this supposed to be C or C++? The two are not the same or freely interchangeable, and the "right" answer will be different for each.

If this is C, then be aware you cannot assign strings to arrays using the '=' operator; you must either use strcpy() or strncpy():

/**
 * In your snippet above, you're just declaring a pointer to entity but not
 * allocating it; is that just an oversight?
 */
entity *aEntity = malloc(sizeof *aEntity); 
...
strcpy(aEntity->fileName, fileNameDir);

or

strncpy(aEntity->fileName, fileNameDir, sizeof aEntity->fileName);

with appropriate checks for a terminating nul character.

If this is C++, you should be using the std::string type for instead of char* or char[]. That way, you can assign string data using the '=' operator:

struct entity {unsigned long attr; std::string fileName};

entity *aEntity = new entity;
std::string fileNameDir = "...";
...
entity->fileName = fileNameDir;
John Bode
+1 for alerting to the differences between C and C++!
sbi
+1  A: 

The major problem is that you declared a pointer to a struct, but allocated no space to it (unless you left some critical code out). And the other problems which others have noted.

xcramps