How to concat two char * in C? - 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);

Related

Tokenizing string from dynamic array into multiple lines in static 2D char array

I have a dynamic array that holds a string containing '\n' characters, so this string is made up of multiple lines. I'm trying to extract the lines and put them all into a 2D char array and I'm getting segmentation errors.
Here's my code:
char *input_lines = malloc(MAX_LINE_LEN*sizeof(char));
input_lines = extractInput(MAX_LINE_LEN, input_file);
char inputLines_counted[lineCount_input][MAX_LINE_LEN];
char *t = strtok(input_lines, "\n");
for(i = 0; i < lineCount_input; i++) {
strcpy(inputLines_counted[i], t);
// printf("%s\n", inputLines_counted[i]);
t = strtok(NULL, "\n");
}
Upon creating the dynamic array, I use the extractInput(MAX_LINE_LEN, input_file) function to populate the input_lines array with a string containing multiple lines.
Here's the extract function:
char *extractInput(int len, FILE *file) {
char tmp[len];
char *pos;
char *input_lines = malloc(len*sizeof(char));
char *lines;
while(fgets(tmp, len, file)) {
// if((pos = strchr(tmp, '\n')) != NULL) {
// *pos = ' ';
// }
input_lines = realloc(input_lines, (strlen(input_lines) + len)*sizeof(char));
strcat(input_lines, tmp);
}
return input_lines;
}
Why am I getting segfaults here?
The function call
input_lines = realloc(input_lines, (strlen(input_lines) + len)*sizeof(char));
takes your current allocated memory block and expands it, if it can. you should check the return value of realloc, it may fail.
btw when you allocate memory in C, you always need to have space for the ending \0.
see what happens with this file
hello\n
world\n
The first fgets reads in hello\n into tmp.
you now do realloc even though it is unnecessary, input_lines is already pointing to a buffer that could hold the string
char *input_lines = malloc(MAX_LINE_LEN*sizeof(char));
now with your realloc
input_lines = realloc(input_lines, (strlen(input_lines) + len)*sizeof(char));
you do strlen(input_lines) + len so you make the buffer strlen("hello\n") + len long.
but the important thing you need to notice is the following line
strcat(input_lines, tmp);
you have not initialized the memory that input_lines is pointing to, it can contain anything even \0's so your strcat could potentially put the string anywhere in the buffer and cause the error you describe.
Either do a memset or use calloc when you allocate the buffer.
If you use realloc you should keep track of the total size that you have allocated and how much you are using of it, before you copy into the buffer check if there is enough room. If not, add a certain number of bytes to the buffer.
I also noticed you read from the file line by line, then you concatenated the lines together to later use strtok to divide them again. It would be more efficient to return an array of lines.

What is the difference between kmemdup_nul() and kstrndup() in Linux?

They are similar functions, but what is the exact difference between them? The Linux documentation states:
Note: Use kmemdup_nul() instead if the size is known exactly.
Both functions allocate the needed memory through kmalloc() and then place a NUL terminator at the end of the allocated buffer. The only difference between the two is that kstrndup() first calls strnlen() to calculate the length of the string and therefore the needed size, thus scanning the string.
You can see kmemdup_nul() as an optimized version of kstrndup(). If you already know the length of the string, you can avoid the initial scanning and just use kmemdup_nul() passing in the length as argument. This saves time since the string doesn't need to be scanned, and it's the reason why you see that note.
Moreover, kstrndup() saves space if the string is shorter than max, so in case you don't know the length of the string, even though kmemdup_nul() would work as well, you might want to call kstrndup() instead to potentially save space.
You can clearly see from the code that the only difference between the two functions is the call to strnlen(). Here's the source code, from mm/util.c:
/**
* kstrndup - allocate space for and copy an existing string
* #s: the string to duplicate
* #max: read at most #max chars from #s
* #gfp: the GFP mask used in the kmalloc() call when allocating memory
*
* Note: Use kmemdup_nul() instead if the size is known exactly.
*
* Return: newly allocated copy of #s or %NULL in case of error
*/
char *kstrndup(const char *s, size_t max, gfp_t gfp)
{
size_t len;
char *buf;
if (!s)
return NULL;
len = strnlen(s, max);
buf = kmalloc_track_caller(len+1, gfp);
if (buf) {
memcpy(buf, s, len);
buf[len] = '\0';
}
return buf;
}
EXPORT_SYMBOL(kstrndup);
/**
* kmemdup_nul - Create a NUL-terminated string from unterminated data
* #s: The data to stringify
* #len: The size of the data
* #gfp: the GFP mask used in the kmalloc() call when allocating memory
*
* Return: newly allocated copy of #s with NUL-termination or %NULL in
* case of error
*/
char *kmemdup_nul(const char *s, size_t len, gfp_t gfp)
{
char *buf;
if (!s)
return NULL;
buf = kmalloc_track_caller(len + 1, gfp);
if (buf) {
memcpy(buf, s, len);
buf[len] = '\0';
}
return buf;
}
EXPORT_SYMBOL(kmemdup_nul);

How can I store lots of strings with different lengths in the same char array without specifying its size?

I'll implement a dictionary that is basically an array of structures.
Here's the structure:
struct WORD {
char* word;
struct WORD* next;
};
And the array that'll help me reach them:
struct WORD* dictionary[26];
The number 26 stands for 26 letters of the alphabet. We need a linked-list for every single letter, bu that is irrelevant now.
The problem is, I need the char* variable in the structure to be able to store words of different lengths. If I leave it as char*, fscanf fuction doesn't work while reading the words from the file onto the structure. If I make it char word[10] for example, I'd be wasting space for shorter words.
What am I supposed to do?
If I leave it as char*, fscanf function doesn't work
You need to leave it as char*, and dynamically allocate the string.
Start with a buffer of some large size, say, 1024. Read the word into that buffer using fscanf with a limit. After that, copy the buffer into a dynamically allocated space for the word:
char buf[1024];
WORD *head = NULL;
while (fscanf(f, "%1023s", buf) == 1) {
size_t len = strlen(buf);
if (!len) continue; // Ignore empty words
WORD *node = malloc(sizeof(WORD));
// Copy the word
node->word = malloc(len+1); // One for '\0' terminator
strcpy(node->word, buf);
// Attach as the new head
node->next = head;
head = node;
}
Note: You can score some points for style with flexible array member: make word an array with no size, and allocate it together with the node itself, like this:
struct WORD {
struct WORD* next;
char word[];
};
...
char buf[1024];
WORD *head = NULL;
while (fscanf(f, "%1023s", buf) == 1) {
size_t len = strlen(buf);
if (!len) continue; // Ignore empty words
WORD *node = malloc(sizeof(WORD)+len+1);
// Copy the word
strcpy(node->word, buf);
// Attach as the new head
node->next = head;
head = node;
}
Now you have a single allocation instead of two.
You can use a temporary buffer with a fixed length and store the word there, so that you can figure out the length. Once you figured out the length, use malloc to allocate the exact number of bytes to the word pointer, and copy it. something like this:
char tempBuffer[20 + 1] = {0}; //Null terminates the string
fsanf(file, "%s", tempBuffer); // read the string
word = (char*) malloc(sizeof(char) * strlen(tempBuffer)); //allocate memory
strcpy(word, (char*) tempBuffer); //copy the string
You will need to allocate your string dynamically. The function scanf() can do this for you using the m format specifier (on POSIX systems that have implemented this feature).
Here is an example:
#include <stdio.h> /* printf, scanf */
#include <stdlib.h> /* free, EXIT */
int main(void)
{
char *str;
fscanf(stdin, "%ms", &str); /* Replace stdin with your FILE * */
printf("%s", str);
free(str);
return EXIT_SUCCESS;
}
Of course you may have noted you need to pass a pointer of your char array to the scanf function so the array can be allocated.
man 3 scanf
An optional 'm' character. This is used with string conversions (%s,
%c, %[), and relieves the caller of the need to allocate a
corresponding buffer to hold the input: instead, scanf() allocates a
buffer of sufficient size, and assigns the address of this buffer to
the corresponding pointer argument, which should be a pointer to a
char * variable (this variable does not need to be initialized before
the call). The caller should subsequently free(3) this buffer when it
is no longer required.
Edit:
As mentioned in comments, the m flag does not exist in standard C.
The best way I can think of would be read the input using a buffer & realloc.
Or you could simply use POSIX's getline() function to get the input (which allocates the string by itself if you need it to).

Assign a string field of a struct in 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;

How do I get strcpy destination string just big enough?

I've resumed C coding for fun after a several year absence.
I've given myself an exercise to safely copy text from standard input to strings using fgets(), and copy to a string just big enough, i.e. only with enough capacity to hold the no. of chars I've actually typed, ultimately to make lists, stacks etc. from scratch, in other words, playing with pointers.
The only way I've managed this smells of kludge to me, as I'm defining the destination string variable for strcpy() late in the control flow. Is there a more elegant/dynamic way to do this?
#inlcude <stdio.h>
#include <string.h>
#define MAXLENGTH 20
int main(int argc, char *argv[]) {
char message[MAXLENGTH];
printf("Enter a string: \n");
fgets(message, MAXLENGTH, stdin);
/* various tests here, omitted for brevity */
char destinationString[strlen(message)];
/*
* Just testing to prove that
* the strlen() of the destination
* string is LESS than MAXLENGTH
*/
printf("Here's the strlen() of destinationString: %lu\n", strlen(destinationString));
printf("Here's the sizeof() destinationString: %lu,\n" sizeof(destinationString));
printf("Here's the contents of the copy: %s", destinationString);
return 0;
}
You can certainly do this dynamically by using malloc.
Consider something like this:
int main(int argc, char *argv[]) {
char *destinationString;
/* ... */
/* Don't forget to allocate one extra byte for the termination character */
destinationString = malloc(strlen(message) + 1);
if (!destinationString)
return -1;
strcpy(destinationString, message);
/* Note: Normally, you should probably use strncpy to avoid overflow
but here, we're sure that there's enough space so strcpy is acceptable */
/* ... */
free(destinationString); /* When you're done using it */
/* ... */
}
I also pointed this out in the comments but to re-iterate, you actually need to allocate strlen(message) + 1 bytes in your destination string buffer or else it will overflow. The extra character is to store the null termination character at the end of C strings.
Code has a number of choices. Here are 2:
malloc() and later free() right sized memory similarly answered by #tangrs. Note that sizeof() destinationString will be the size of a pointer.
size_t size = strlen(message) + 1;
char *destinationString = malloc(size);
memcpy(destinationString, message, size);
Use variable length array, VLA, available in C99 and optionally in C11.
VLA approach with code clean-up
#include <string.h>
#define MAXLENGTH 20
int main(int argc, char *argv[]) {
char message[MAXLENGTH];
printf("Enter a string: \n");
if (fgets(message, sizeof message, stdin) == NULL) {
return -1;
}
// Use type `size_t`
size_t size = strlen(message) + 1;
char destinationString[size];
memcpy(destinationString, message, size);
// Notice "%zu"
// `sizeof destinationString` is the size of an array
printf("Here's the strlen() of destinationString: %zu\n", strlen(destinationString));
printf("Here's the sizeof() destinationString: %zu,\n" sizeof destinationString);
printf("Here's the contents of the copy: \"%s\"", destinationString);
return 0;
}
Input "Hello!" Enter
Here's the strlen() of destinationString: 8
Here's the sizeof() destinationString: 9,
Here's the contents of the copy: "Hello!
"
On my system the inputs ended with a "\r\n". To rid the buffer of those potential pesky characters, use:
fgets(message, sizeof message, stdin);
buffer[strcspn(message, "\r\n")] = '\0';
size_t size = strlen(message) + 1;
...

Resources