Assign a string field of a struct in C - c

I try to write a program using this structure containing strings :
typedef struct s_conf
{
char *shell1;
char *shell2;
char *shell3;
char *shell4;
char *server_ip;
} t_conf;
Parsing a config text file line per line, I get this information and I store it into variables such as line1 and line4. Now I want to assign my struct fields the values of the variables line1 and line4:
char *line1 = "/var/www/host/current/app/console robot:file";
char *line4 = "192.168.00.00";
t_conf *conf;
if ((conf = malloc(sizeof(t_conf))) == NULL)
{
fprintf(stderr, "Malloc error\n");
return (-1);
}
strcpy(conf->shell1, line1);
strcpy(conf->server_ip, line4);
printf("line1 : '%s'\n"; line1);
printf("line4 : '%s'\n"; line4);
printf("t_conf->shell1 : '%s'\n", conf->shell1);
printf("t_conf->server_ip : '%s'\n", conf->server_ip);
The output :
line1 : '/var/www/host/current/app/console robot:file'
line4 : '192.168.00.00'
t_conf->shell1 : '/var/www/host/current/app'
t_conf->server_ip : '192.168.00.00'
How to correctly assign the c string t_conf->shell1 ?
I try other functions like memcpy(), strdup() and allocate the variable with malloc : t_conf->shell1 = malloc(strlen(line1) + 1) but it gives me the same result, I lose a portion of line1 ?

I try to write a program using this structure containing strings :
struct s_conf below contains 5 pointers. It does not contains any strings. With the C standard library, a string is an array of characters up to and including a final null character ('\0'). For your code to work, memory for these arrays are needed - someplace.
typedef struct s_conf {
char *shell1;
char *shell2;
char *shell3;
char *shell4;
char *server_ip;
} t_conf;
strcpy(conf->shell1, line1); fails because conf->shell1 does not yet have a value pointing to available memory for the copy.
Populate these 5 pointers with values pointing to memory containing the needed data.
// allocate memory for the structure
conf = malloc(sizeof *conf);
assert(conf);
// Simply copy the pointer if `line1` will exist for as long as `conf`.
conf->shell1 = line1;
// or
// Create an allocated copy.
conf->shell1 = strdup(line1);
// With this method, be sure to free the memory before freeing conf
...
free(conf->shell1);
free(conf);
strdup() is not a standard library function, yet very common. Make an equivalent if needed. Example: (tailor to your needs)
char *my_strdup(const char *s) {
if (s) {
size_t sz = strlen(s) + 1;
char *dest = malloc(sz);
if (dest) {
return memcpy(dest, src, sz);
}
}
return NULL;
}

strcpy(conf->shell1, line1);
You need space to store line1
Furthermore (as pointed out by #cat in comments) strcpy is dangerous and must be avoided in production code, an alternative is strdup (non standard), or snprintf:
size_t size = strlen(line1) + 1;
conf->shell1 = malloc(size);
snprintf(conf->shell1, size, "%s", line1);
The space should be returned with free(conf->shell1); when it is no longer needed.
Same for conf->server_ip
Note that if you don't need to modify those strings, you don't need to copy, just assign:
conf->shell1 = line1;

Related

How do I use char** ? (pointer to an array of chars)

So I'm trying to make a char**, I fully understand how it works in the background and all that stuff but I don't seem to understand how to write the code for it. I want to make a pointer to an array of chars which has a name in it. I need help with storing a string in it (using strcpy() ) and print it after that.
char** name = (char**)malloc((strlen("MyName") + 1) * sizeof(char*));
strcpy(name, "MyName"); // I get an error right here
If you really want a pointer to an char array, you could do the following:
char** name = (char**)malloc(sizeof(char*)); //initialize the pointer
*name = (char*)malloc((strlen("MyName") + 1) * sizeof(char)); //initialize the array
strcpy(*name, "MyName");
So I'm trying to make a char**, I fully understand how it works in the
background and all that stuff but I don't seem to understand how to
write the code for it.
Umm... No, not quite.
To declare a pointer-to-char, you simply decalre:
char *name = malloc (strlen("MyName") + 1);
Why? When you make your call to malloc, malloc allocates a block of memory providing strlen("MyName") + 1 bytes and returns the starting address to that block of memory -- which you assign to name. You then can copy "MyName" to name (with 1-byte remaining for the nul-terminating character). The approach would be:
size_t len = strlen ("MyName");
char *name = malloc (len + 1); /* allocate len + 1 bytes */
if (name == NULL) { /* validate EVERY allocation */
perror ("malloc-name");
/* handle error by returning or exiting */
}
memcpy (name, "MyName", len + 1); /* no need to scan again for \0 */
/* do something with name - here */
free (name); /* don't forget to free name when you are done */
What then does char** do?
When you are dealing with a pointer-to-pointer-to-char, you must first allocate for some number of pointers, then you can allocate and assign a block of memory to each of the pointers and use each pointer just as you have used name above.
For example:
/* array of ponters to string-literals for your source of strings */
char *band[] = { "George", "Ringo", "Paul", "John" };
char **names;
size_t nmembers = sizeof band / sizeof *band;
/* allocate nmembers pointers */
names = malloc (nmembers * sizeof *names);
if (names == NULL) { /* validate EVERY allocation */
perror ("malloc-name_pointers");
/* handle error by returning or exiting */
}
/* now loop allocating for each name and copy */
for (size_t i = 0; i < nmembers; i++) {
size_t len = strlen (band[i]); /* get length */
names[i] = malloc (len + 1); /* allocate */
if (names[i] == NULL) { /* validate EVERY allocation */
perror ("malloc-names[i]");
/* handle error by returning or exiting */
}
memcpy (names[i], band[i], len + 1);/* no need to scan again for \0 */
}
/* output each */
for (size_t i = 0; i < nmembers; i++)
printf ("member[%zu]: %s\n", i + 1, names[i]);
Freeing names is a two step process. You must free the memory allocated to each of the names pointers and then free the pointers themselves, e.g.
for (size_t i = 0; i < nmembers; i++)
free (names[i]); /* free each allocated names[i] */
free (names); /* free pointers */
Now hopefully you more closely "... fully understand how it works". Let me know if you have any questions.
First thing you should understand is that declaring a variable as a single pointer or a double pointer (or any other n pointer) doesn't actually tell whether the underlying variable holds a single value or an array of values.
Single pointer points to a memory address on which actual value is stored. Double pointer points to a memory address on which single pointer is stored, and so on.
Now, to make a pointer to an array of char pointers you can use a single char pointer (char*) but I recommend to use double char pointer (char**) for maintainability purposes.
Consider the following code:
char** names = (char**)malloc(100 * sizeof(char*));
It will allocate memory space for 100 char pointers (char*) on heap and return you a double pointer (char**) to the first single pointer (char*) in that memory space. This means you will be able to save 100 char pointers (or 100 names in your case) inside that memory space. Then you can use them like this:
char* name0 = "First Name"; // Saved on stack
char* name1 = malloc((strlen("Second Name") + 1) * sizeof(char)); // Saved on heap
strcpy(name1, "Second Name");
names[0] = name0;
names[1] = name1;
Also, please note that when saving a string on heap you need to add one more place for null character (manually).

Segfault when assigning to char* in struct

So I have a struct "sequence" that has a char* in it. When I try and create a sequence, whenever I try and change the char* it segfaults. Here is the related code. The struct:
typedef struct _sequence {
unsigned int length;
unsigned char* bytes;
} Sequence;
The constructor:
Sequence* newSequence(unsigned char firstByte) { //Creates new sequence, allocates memory
printf("Creating new Sequence\n");
Sequence* seq = (Sequence*)malloc(sizeof(Sequence));
printf("Have new sequence\n");
seq->length = 1;
printf("Set length\n");
seq->bytes[0] = firstByte;
printf("Set variables\n");
return seq;
}
Now I have a main function here just for testing purposes, this file will in the end not have a main function. But here is what i used to testing:
int main() {
char test[] = "ab";
printf("Testing sequences!\n");
Sequence* newSeq = newSequence(test[0]);
printf("Made new sequence!\n");
outputSequence(newSeq, stdout);
printf(" <-- new Sequence created\n");
return 0;
}
The printfs are again for testing purposes. It always prints out all the way up to "Set length\n" in the constructor, then segfaults. What am I doing wrong? Thank you!
You allocated apace for the structure correctly, but you didn't allocate any space for the buffer pointed to by the bytes element.
This line invokes undefined behavior because bytes is uninitialized:
seq->bytes[0] = firstByte;
You need to also allocate a buffer and point seq->bytes to it.
Sequence* seq = malloc (sizeof(Sequence));
Here you allocate memory space for one char * , and one int, but you need to allocate space for what you want to store on what is pointed at by your char *, this way :
seq->bytes = malloc (my_string_size);
Only then can you start storing characters in your allocated chunk of memory.
Edit : for instance, to store one single character, you could do :
seq->bytes = malloc(1);
seq->bytes[0] = firstByte;
to use it as a single character. But the good habit in C to manipulate string is to always leave one char more, in that fashion :
seq->bytes = malloc(2);
seq->bytes[0] = firstByte;
seq->bytes[1] = '\0';
The 2nd method looks more like a real 'string' in C.

How to create a copy of strcat?

I have to create a copy of some elements of the standard library in C and I have to create a copy of strcat. So I have to create a function that concatenate two strings in C. I know arrays in C can't change the allocated size. The only fonction i'm allowed to use is copies i made of strlen, strstr, and write() ... My code looks like this :
char *my_strcat(char *dest, char *src)
{
int dest_size;
int src_size;
int current_pos;
int free_space;
int pos_in_src;
src_size = my_strlen(src);
dest_size = my_strlen(dest);
while (dest[current_pos] != '\0')
current_pos = current_pos + 1;
free_space = dest_size - current_pos;
if (free_space < src_size)
return (0);
while (src[pos_in_src] != '\0')
{
dest[current_pos] = src[pos_in_src];
pos_in_src = pos_in_src + 1;
current_pos = current_pos + 1;
}
return (dest);
}
But I don't know how to declare my dest and src in the main.
I don't know how to create an array with a big size, declare it as a string like dest = "Hello\0" but this array has to still contains more than 6 characters.
Can you help me please ?
char dest[19] = "epite";
char *src = "chor42spotted";
my_strcat(dest, src);
Also, read the man for strcat(3)
the dest string must have enough space for the result.
https://linux.die.net/man/3/strcat
So your function is behaving incorrectly, you do not need to check that you have enough free space in dest
You want a function mystrcat which behaves exactly like stdlib strcat.
So the prototype is
/*
concatenate src to dest
dest [in / out] - the string to add to (buffer must be large enough)
src [in] - the string to concatenate.
Returns: dest (useless little detail for historical reasons).
*/
char *mystrcat(char *dest, const char *src);
Now we call it like this
int main(void)
{
char buff[1024]; // nice big buffer */
strcpy(buff, "Hello ");
mystrcat(buff, "world");
/* print the output to test it */
printf("%s\n", buff);
return 0;
}
But I'm not going to write mystrcat for you. That would make your homework exercise pointless.
The 1st parameter of the array simply has to be large enough to contain both strings + one null terminator. So if you for example have "hello" and "world", you need 5 + 5 +1 = 11 characters. Example:
#define LARGE_ENOUGH 11
int main (void)
{
char str[LARGE_ENOUGH] = "hello";
my_strcat(str, "world");
puts(str); // gives "helloworld"
}
In real world applications, you would typically allocate space for the array to either be same large number (couple of hundred bytes) or with a length based on strlen calls.
As for the implementation itself, your solution is needlessly complicated. Please note that the real strcat leaves all error checking to the caller. It is most likely implemented like this:
char* strcat (char* restrict s1, const char* restrict s2)
{
return strcpy(&s1[strlen(s1)], s2);
}
The most important part here is to note the const-correctness of the s2 parameter.
The restrict keywords are just micro-optimizations from the C standard, that tells the compiler that it can assume that the pointers point at different memory areas.
If you wish to roll out your own version with no library function calls just for fun, it is still rather easy, you just need two loops. Something like this perhaps:
char* lolcat (char* restrict s1, const char* restrict s2)
{
char* s1_end = s1;
while(*s1_end != '\0') // find the end of s1
{
s1_end++;
}
do // overwrite the end of s1 including null terminator
{
*s1_end = *s2;
s1_end++;
s2++;
} while(*s1_end != '\0'); // loop until the null term from s2 is copied
return s1;
}

Return a string on C

I'm getting a core dump that I have no clue how to solve. I have searched other questions and googled my problem but I just can't figure out how to solve this...
Here is the code:
const char checkExtension(const char *filename)
{
const char *point = filename;
const char *newName = malloc(sizeof(filename-5));
if((point = strrchr(filename,'.palz')) != NULL )
{
if(strstr(point,".palz") == 0)
{
strncpy(newName, filename, strlen(filename)-5);
printf("%s\n",newName ); // the name shows correctly
return newName; // Segmentation fault (core dumped)
}
}
return point;
}
The function was called char checkExtensions(const char *filename). I added the const due the solutions that I have found online but so far I haven't been able to make it work...
Thank you in advance for the help!
You have many problems with your code. Here are some of them:
Your function returns char which is a single character. You need to return a pointer to an array of characters, a C string.
You don't allocate the right amount of memory. You use sizeof() on a pointer which yields the size of a pointer.
You make it impossible for the caller to know whether or not to deallocate memory. Sometimes you heap allocate, sometimes not. Your approach will leak.
You pass '.palz', which is a character literal, to strrchr which expects a single char. What you mean to pass is '.'.
A better approach is to let the caller allocate the memory. Here is a complete program that shows how:
#include <string.h>
#include <stdio.h>
void GetNewFileName(const char *fileName, char *newFileName)
{
const char *dot = strrchr(fileName, '.');
if (dot)
{
if (strcmp(dot, ".palz") == 0)
{
size_t len = dot - fileName;
memcpy(newFileName, fileName, len);
newFileName[len] = 0;
return;
}
}
size_t len = strlen(fileName);
memcpy(newFileName, fileName, len);
newFileName[len] = 0;
return;
}
int main(void)
{
char fileName[256];
char newFileName[256];
strcpy(fileName, "foo.bar");
GetNewFileName(fileName, newFileName);
printf("%s %s\n", fileName, newFileName);
strcpy(fileName, "foo.bar.palz");
GetNewFileName(fileName, newFileName);
printf("%s %s\n", fileName, newFileName);
strcpy(fileName, "foo.bar.palz.txt");
GetNewFileName(fileName, newFileName);
printf("%s %s\n", fileName, newFileName);
return 0;
}
Output
foo.bar foo.bar
foo.bar.palz foo.bar
foo.bar.palz.txt foo.bar.palz.txt
Note that strcmp compares sensitive to letter case. On Windows file names are insensitive to case. I will leave that issue for you to deal with.
By letting the caller allocate memory you allow them to chose where the memory is allocated. They can use a local stack allocated buffer if they like. And it's easy for the caller to allocate the memory because the new file name is never longer than the original file name.
This is most probably your problem:
const char *newName = malloc(sizeof(filename-5));
First, filename is of type const char *, which means that (filename - 5) is also of this type. Thus, sizeof(filename - 5) will always return the size of the pointer datatype of your architecture (4 for x32, 8 for x64).
So, depending on your architecture, you are calling either malloc(4) or malloc(8).
The rest of the code doesn't even compile and it has serious string manipulation issues, so it's hard to tell what you were aiming at. I suppose the strncpy() was copying too much data into newName buffer, which caused buffer overflow.
If your goal was to extract the filename from a path, then you should probably just use char *basename(char *path) for that.
Several pretty major problems with your code. Making it up as I type, so it may not fix everything first time right away. Bear with me.
You need to return a char *, not a char.
const char checkExtension(const char *filename)
{
const char *point = filename;
You malloc memory but the instruction flow does not guarantee it will be freed or returned.
sizeof(filename) should be strlen(filename), minus 5 (sans extension) but +1 (with terminating 0).
const char *newName = malloc(sizeof(filename-5));
strrchr searches for a single character. Some compilers allow "multibyte character constants", but they expect something like 2 -- not five. Since you know the length and start of the string, use strcmp. (First ensure there are at least 5 characters. If not, no use in testing anyway.)
if((point = strrchr(filename,'.palz')) != NULL ) {
Uh, strstr searches for a string inside a string and returns 0 if not found (actually NULL). This contradicts your earlier test. Remove it.
if(strstr(point,".palz") == 0)
{
strncpy copies n characters, but famously (and documented) does not add the terminating 0 if it did not get copied. You will have to this yourself.
.. This is actually where the malloc line should appear, right before using and returning it.
strncpy(newName, filename, strlen(filename)-5);
printf("%s\n",newName ); // the name shows correctly
return newName; // Segmentation fault (core dumped)
}
}
You return the original string here. How do you know you need to free it, then? If you overwrote a previous char * its memory will be lost. Better to return a duplicate of the original string (so it can always be freed), or, as I'd prefer, return NULL to indicate "no further action needed" to the calling routine.
return point;
}
Hope I did not forget anything.
There are several problems with your code:
Wrong return type:
const char checkExtension(const char *filename){
You need to return a pointer (const char *), not a single character.
Not enough memory:
const char checkExtension(const char *filename){
const char *newName = malloc(sizeof(filename-5));
You are allocating the size of a pointer (char *), which is typically 4 or 8. You need to call strlen() to find out the size of the string:
Multibyte character:
if((point = strrchr(filename,'.palz')) != NULL ) {
'.palz' is a multibyte character literal. While this is allowed in C, its value is implementation-defined and might not do what you expect. String literals use double quotes (".palz").
No terminating zero:
strncpy(newName, filename, strlen(filename)-5);
Note that strncpy() doesn't necessarily null-terminate the target string. It write at most strlen(filename)-5 characters. If the source string contains more characters (as in your case), it will not write a terminating zero.
I'm not sure what exactly you're trying to do. Perhaps something like this:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
const char *checkExtension(const char *filename)
{
int len = strlen (filename)-5;
char *newName = NULL; /* return NULL on allocation failure. */
if (len > 0 && !strcmp (filename+len, ".palz")) {
newName = malloc (len+1);
if (newName) {
memcpy (newName, filename, len);
newName[len] = 0;
}
}
return newName;
}
int main (int ac, char **av)
{
if (ac > 1) {
const char *p = checkExtension (av[1]);
puts (p ? p : "NULL");
} else {
puts ("?");
}
return 0;
}
Multiple errors here. You have not said what you are trying to achieve, that has to be implied from the code. You have declared point and newName as const, yet reassigned with a value. You have tested strstr() == 0 when it should be strstr() == NULL. You have called strrchr(filename,'.palz') but sent a string instead of a char. Then you have returned the local variable point which goes out of scope before you get a chance to use it, because it was not declared as static. So it's irrelevant whether you returned a char or a char pointer.
char *checkExtension(const char *filename) {
// if filename has extension .palz return a pointer to
// the filename stripped of extension or return NULL
char *point;
static char newName[512];
strncpy(newName, filename, 512);
if ((point = strstr(newName, ".palz")) != NULL ) {
if (strlen (point) == 5) {
*point = 0; // string terminator
// printf("%s\n",newName ); // use only for debugging
return newName;
}
}
return NULL;
}
Alternatively provide a string the function can modify -
char *checkExtension(const char *filename, char *newName) { ... }
Alternatively provide a filename the function can modify -
char *checkExtension(char *filename) {
char *point;
if ((point = strstr(filename, ".palz")) != NULL ) {
if (strlen (point) == 5) {
*point = 0; // string terminator
return filename;
}
}
return NULL;
}

How to concat two char * in C?

I receive a char * buffer which have the lenght of 10.
But I want to concat the whole content in my struct which have an variable char *.
typedef struct{
char *buffer;
//..
}file_entry;
file_entry real[128];
int fs_write(char *buffer, int size, int file) {
//every time this function is called buffer have 10 of lenght only
// I want to concat the whole text in my char* in my struct
}
Something like this :
real[i].buffer += buffer;
How can I do this in C ?
In general, do the following (adjust and add error checking as you see fit)
// real[i].buffer += buffer;
// Determine new size
int newSize = strlen(real[i].buffer) + strlen(buffer) + 1;
// Allocate new buffer
char * newBuffer = (char *)malloc(newSize);
// do the copy and concat
strcpy(newBuffer,real[i].buffer);
strcat(newBuffer,buffer); // or strncat
// release old buffer
free(real[i].buffer);
// store new pointer
real[i].buffer = newBuffer;
You can use strcat(3) to concatenate strings. Make sure you have allocated enough space at the destination!
Note that just calling strcat() a bunch of times will result in a Schlemiel the Painter's algorithm. Keeping track of the total length in your structure (or elsewhere, if you prefer) will help you out with that.
I am not clear. Do you want:
to concatenate every one of the 10 character buffers you receive into one array, pointed at by one real[0].buffer, or
do you want each 10 character buffer to be pointed at by a different real[i].buffer, or
something else?
You will need to allocate enough space for the copy of the buffer:
#include <stdlib.h>
//...
int size = 10+1; // need to allocate enough space for a terminating '\0'
char* buff = (char *)malloc(size);
if (buff == NULL) {
fprintf(stderr, "Error: Failed to allocate %d bytes in file: %s, line %d\n",
size, __FILE__, __LINE__ );
exit(1);
}
buff[0] = '\0'; // terminate the string so that strcat can work, if needed
//...
real[i].buffer = buff; // now buffer points at some space
//...
strncpy(real[i].buffer, buffer, size-1);

Resources