This code is a part of a bigger program. I want to create an array of structs. The struct contains two data types: char and char*, each of which gets an input from the user. I'm a rookie and not able to find my way through this. Please help.
Error generated: Segmentation Fault 11.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct {
char ch;
char str[20];
}typ;
typ* add(char* str, char ch);
int main(int argc, char const *argv[]){
typ* arr;
arr = (typ*)malloc(sizeof(typ));
char* name;
name = (char*)malloc(10);
*(name + 0)= 'c';
*(name + 1)= 'a';
*(name + 2)= 'p';
*(name + 3)= '\0';
char c = 'k';
arr = add(name, c);
printf("%c\n", arr->ch );
printf("%s\n", arr->str );
return 0;
}
typ* add(char* str, char ch){
typ tempo;
strcpy(str, tempo.str);
printf("%s\n", str);
tempo.ch = ch;
typ* temp;
*temp = tempo;
return temp;
}
You used arr for the allocated memory, but then you assign it to add's return value, the memory got lost at this point, you have a memory leak.
In the add function, you return a pointer to a variable with automatic storage, which does not exists after the function returns. That's why you are getting a segfault.
So I would allocate the struct in the add function, and returns it:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct {
char ch;
char str[20];
} Item;
Item* add(char* str, char ch);
int main(int argc, char const *argv[]){
int i;
int n = 3; // number of elements in the array
Item** arr = malloc(n * sizeof(Item*));
arr[0] = add("cap", 'k');
arr[1] = add("foo", 'i');
arr[2] = add("bar", 'j');
printf("%c\n", arr[0]->ch );
printf("%s\n", arr[0]->str );
for (i = 0; i < n; i++)
free(arr[i]);
free(arr);
return 0;
}
Item* add(char* str, char ch) {
Item *i = malloc(sizeof *i);
strcpy(i->str, str);
i->ch = ch;
return i;
}
Related
I have a structure inside which char array and int value is maintained. I want to treat this char array as a flat array to store the list of strings and the offset will track the starting position where the string is added in the array.
Structure is shown below:
struct A
{
char element[256];
int offset;
}
Also, I want to delete the strings after performing some operation if found.
Please let me know if this feasible. If yes then how?
Yes, append to a.element[a.offset].
To delete, set a.element[0] to the null byte. C strings end at a null byte.
#include <stdio.h>
#include <string.h>
typedef struct
{
char element[256];
int offset;
} A;
void A_append(A* a, const char *str) {
// Concatenate on the end of element.
strcat(&a->element[a->offset], str);
// Increment the offset to the new end.
a->offset += strlen(str);
}
void A_delete(A* a) {
a->element[0] = '\0';
a->offset = 0;
}
int main() {
A a = { .element = "" };
a.offset = 0;
char string1[] = "one";
A_append(&a, string1);
char string2[] = "two";
A_append(&a, string2);
puts(a.element);
A_delete(&a);
puts(a.element);
}
You can also store a pointer to the end of element. It's the same thing, just more direct.
#include <stdio.h>
#include <string.h>
typedef struct
{
char element[256];
char *end;
} A;
void A_append(A* a, const char *str) {
// Concatenate nto the end of element.
strcat(a->end, str);
// Increment the pointer to the new end.
a->end += strlen(str);
}
void A_delete(A* a) {
a->element[0] = '\0';
a->end = a->element;
}
int main() {
A a = { .element = "" };
a.end = a.element;
char string1[] = "one";
A_append(&a, string1);
char string2[] = "two";
A_append(&a, string2);
puts(a.element);
A_delete(&a);
puts(a.element);
}
Finally, if you want to store a list of strings, not concatenate them, consider storing them as pointers.
Since all we have to go on is the question if it's feasible - and the answer is yes. Here's a way showing that it is:
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct A A;
struct A {
char element[256];
int offset;
};
A *A_create() {
A *a = malloc(sizeof *a);
a->offset = 0;
return a;
}
void A_destroy(A *a) {
free(a);
}
// return false if the string doesn't fit
// true if it's successfully added
bool A_add_string(A *a, const char *str) {
size_t len = strlen(str);
if(a->offset + len >= sizeof a->element) return false;
memcpy(a->element + a->offset, str, len + 1);
a->offset += len + 1;
return true;
}
You can now create an A, add \0 terminated strings to it and finally destroy it:
A *a = A_create();
A_add_string(a, "Hello");
A_add_string(a, "world");
A_destroy(a);
I have been trying to convert a string in array of integers, floats and characters. While I could get it work for integers and floats, there is some problem for characters.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char *s1;
int k, no=5;
char* variable = "R1,R2,R3,R4,R5";
void* value;
s1 = calloc(no,sizeof(char)*81);
for (k=0; k<no; k++) s1[k] = strdup(mchar);
ListChar(variable, s1, no, ",");
memcpy(value, s1, no*sizeof(char)*81);
free(s1);
int i;
for (i = 0; i < no; i++)
printf("%s", value[i]);
printf("\n");
return 0;
}
In the header file I have
#define mchar "A...(81times)"
Implementation:
int ListChar(char *buf, char *list, int maxloop, char* delim)
{
int n = 0;
char *s,*t;
s= strdup(buf);
t= strtok(s,delim);
while ( t && (n<maxloop))
{
if (list!=NULL) list[n] =strdup(t);
n++;
t=strtok(NULL,delim);
}
free(s);
return(n);
}
During the calloc memory assignment when I watch s1 its 0xsomeadress ""
After the for loop s1 becomes 0xsomeadress "Garbage value 81 times"
When s1 is assigned to list its still reads the same garbage value.
And when list [n] = strdup(t) list[0] reads the first block of garbage value like -21 '\221 ṗ'.
t is getting delimited correctly. I even tried initializing char *s1[81] = {"something"} and looping it on j but it wont work, same problem, and I need to free s1 at the end because this function runs for number of times. I did it for integers and floats by list[n]=atoi(t) it works fine. Can anyone suggest me something?
There seems to be a fundamental misunderstanding about how strings work. Your s1 clearly needs to be a char ** and the usage of strdup is incorrect. If s1 is of type char *, then s1[k] is of type char. But strdup returns a char *, so s1[k] = strdup ... is clearly an error which your compiler ought to warn you about. Perhaps you want something like:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void * xmalloc(size_t s);
void
ListChar(const char *buf, char **list, int maxloop, int delim)
{
char set[] = {delim, 0};
for( int n = 0; n < maxloop; n += 1 ){
size_t len = strcspn(buf, set);
list[n] = xmalloc(len + 1);
memcpy(list[n], buf, len);
buf += len + 1;
}
}
int
main(int argc, char **argv)
{
int delim = ',';
(void)argc; /* Suppress compiler warning */
while( *++argv ){
char **s1;
int k, num = 1;
char *input = *argv;
for( const char *p = input; *p; p += 1 ){
if( *p == delim ){
num += 1;
}
}
s1 = xmalloc(num * sizeof *s1);
ListChar(input, s1, num, delim);
for( int i = 0; i < num; i += 1 ){
printf("%s\n", s1[i]);
}
free(s1);
}
return 0;
}
void *
xmalloc(size_t s)
{
void *rv = malloc(s);
if( rv == NULL ){
perror("malloc");
exit(EXIT_FAILURE);
}
return rv;
}
Note that the above code scans each string twice, which is not ideal. Rather than scanning the string to find the number of delimiters and then parsing the string, it would be better to do both in one pass. But for the purposes of demonstrating how to break up the string, that seems like unnecessary complexity. (Though it's actually simpler, IMO)
RHEL6
I'm trying to implement a perl split funciton in a C subroutine which dynamically builds the array of strings. My attempt fails with a segfault. But it does not fail if I comment out the printf statement in the for loop (perhaps implying that the segfault is in where its getting built as opposed to how)
Here it is...
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int split(char *s, char **arr);
void main(int argc, char* argv[])
{
int x;
int arrsz;
char str[]="aaa:bbb:ccc";
char **arr;
arrsz=split(str,arr);
for(x=0;x<arrsz;x++) {
printf("%s\n",arr[x]);
}
exit(0);
}
/***********************************/
int split(char *str, char **arr) {
int arrsz=0;
char delim[2] = ":";
char *tok;
arr = malloc(sizeof(char **));
arr[0] = malloc(1);
arr[0] = '\0';
tok = strtok(str,delim);
while(tok != NULL) {
arrsz++;
arr = (char **)realloc(arr,(arrsz*sizeof(char *))+1);
arr[arrsz-1] = malloc((sizeof(char)*strlen(tok))+1);
strcpy(arr[arrsz-1],tok);
arr[arrsz]=malloc(1);
arr[arrsz]='\0';
tok = strtok(NULL,delim);
}
return(arrsz);
}
I think the problem is in how I'm passing "arr" to the split function or how it's being received and used in the function. I say this because if I move the body of the function to main, it works there.
I tried dealing with arr inside the functions as it it was a (char ***), but that didn't work.
Can a C expert out there set me straight ?
The main error is that you should pass a pointer to the strings list to the split function, not the strings list itself, so you should use an ***arr:
int split(char *str, char ***arr);
And you should use & to pass the pointer in main:
...
arrsz=split(str,&arr);
...
In the function you could use a double pointer to avoid confusion and at the end assign that pointer to the parameter:
int split(char *str, char ***arrreturn) {
char **arr; //Use this strings list to add the strings
...
*arreturn = arr;
return(arrsz);
}
-You should not call realloc anytime you need to insert a string, but you could oversize it and increment its dimension if you need.
-I cannot see the need of assign '\0' at the end of the list if you have a variable with the length
-You can use strdup instead of malloc-strcpy funcs:
char *first = "ciao";
char *str = malloc(strlen(first) * sizeof(char));
strcpy(str, first);
Is equal to:
char *first = "ciao";
char *str = strdup(first);
I corrected your code:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int split(char *str, char ***arrreturn);
void main(int argc, char *argv[]) {
int x;
int arrsz;
char str[] = "aaa:bbb:ccc";
char **arr;
arrsz = split(str, &arr);
for (x = 0; x < arrsz; x++) {
printf("%s\n", arr[x]);
}
exit(0);
}
/***********************************/
int split(char *str, char ***arrreturn) {
int arrsz = 1;
int len = 0;
char delim[2] = ":";
char *tok;
char **arr;
arr = malloc(sizeof(char **));
tok = strtok(str, delim);
while (tok != NULL) {
len++;
if (len >= arrsz) {
arrsz *= 2;
arr = realloc(arr, arrsz * sizeof(char **));
}
arr[len - 1] = strdup(tok);
tok = strtok(NULL, delim);
}
*arrreturn = arr;
return (len);
}
There are a few bugs. I've annotated and [partially] fixed bugs. It will still segfault. I added a refactored version that will work correctly.
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int split(char *s, char **arr);
void main(int argc, char* argv[])
{
int x;
int arrsz;
char str[]="aaa:bbb:ccc";
char **arr;
#if 1
#endif
arrsz=split(str,arr);
for(x=0;x<arrsz;x++) {
printf("%s\n",arr[x]);
}
exit(0);
}
/***********************************/
int split(char *str, char **arr) {
int arrsz=0;
char delim[2] = ":";
char *tok;
// NOTE/BUG: this function only changes arr within the function and does
// _not_ propagate it to the caller
arr = malloc(sizeof(char **));
// NOTE/BUG: this is replaced in the loop and leaks memory
#if 0
arr[0] = malloc(1);
arr[0] = '\0';
#endif
tok = strtok(str,delim);
while(tok != NULL) {
arrsz++;
// NOTE/BUG: this is incorrect -- it only adds a byte instead of another
// pointer (i.e. it doesn't allocate enough)
#if 0
arr = (char **)realloc(arr,(arrsz*sizeof(char *))+1);
#else
arr = (char **)realloc(arr,sizeof(char *) * (arrsz + 1));
#endif
#if 0
arr[arrsz-1] = malloc((sizeof(char)*strlen(tok))+1);
strcpy(arr[arrsz-1],tok);
#else
arr[arrsz-1] = strdup(tok);
#endif
// NOTE/BUG: this is wrong and leaks memory
#if 0
arr[arrsz]=malloc(1);
arr[arrsz]='\0';
#endif
tok = strtok(NULL,delim);
}
#if 1
arr[arrsz] = NULL;
#endif
return(arrsz);
}
But, as written, your function doesn't update caller's value of arr.
To fix your function, split would need arr to be defined as a "three star" pointer (e.g. char ***arr) which is considered cumbersome and very bad practice.
So, a better/simpler solution is to refactor the function and pass back arr as return (e.g. char **split(char *str,int *sizrtn):
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char **split(char *s, int *arsiz);
int main(int argc, char* argv[])
{
int x;
int arrsz;
char str[]="aaa:bbb:ccc";
char **arr;
arrsz = 0;
arr = split(str,&arrsz);
for(x=0;x<arrsz;x++) {
printf("%s\n",arr[x]);
}
return 0;
}
/***********************************/
char **split(char *str, int *sizrtn)
{
int arrsz=0;
const char *delim = ":";
char *tok;
char **arr = NULL;
tok = strtok(str,delim);
while (tok != NULL) {
arrsz++;
arr = realloc(arr,sizeof(char *) * (arrsz + 1));
arr[arrsz - 1] = strdup(tok);
tok = strtok(NULL,delim);
}
if (arr == NULL)
arr = malloc(sizeof(*arr));
arr[arrsz] = NULL;
*sizrtn = arrsz;
return arr;
}
To modify an object in the caller's scope you must pass a pointer to the object - so you need one more level of indirection. There is also at least one semantic error in the implementation - assigning '\0' to the pointer returned by malloc(), will both invalidate the pointer and cause a memory leak.
Change split() prototype to:
int split( char* s, char*** arr ) ;
Then call it thus:
arrsz = split( str, &arr ) ;
And change the implementation:
int split( char* str, char*** arr )
{
int arrsz = 0 ;
char delim[2] = ":" ;
char* tok ;
*arr = malloc(sizeof(char**));
*arr[0] = malloc(1);
**arr[0] = '\0'; // <<< This is fixed too
tok = strtok( str, delim ) ;
while( tok != NULL )
{
arrsz++;
*arr = (char **)realloc(*arr,(arrsz*sizeof(char *))+1);
*arr[arrsz-1] = malloc((sizeof(char)*strlen(tok))+1);
strcpy(*arr[arrsz-1],tok);
*arr[arrsz]=malloc(1);
*arr[arrsz]='\0';
tok = strtok(NULL,delim);
}
return(arrsz);
}
There may be other errors I have not spotted, but that is fundamental. Best from hereon debugged using a debugger rather then Q&A.
the following proposed code:
cleanly compiles
performs the desired functionality
properly checks for errors from system functions
eliminates any need to use a *** parameter -- google three star programer as to why that is bad
does not include header files those contents are not used
and now, the proposed code:
//#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char ** split(char *str, size_t *arrsz);
int main( void )
{
size_t x;
size_t arrsz;
char str[]="aaa:bbb:ccc";
char **arr=split(str,&arrsz);
for(x=0;x<arrsz;x++)
{
printf("%s\n",arr[x]);
}
exit(0);
}
/***********************************/
char ** split(char *str, size_t *arrsz)
{
char **arr = NULL;
size_t count = 0;
char delim[2] = ":";
char *tok;
tok = strtok(str,delim);
while(tok != NULL)
{
count++;
char **temp = realloc(arr,(count*sizeof(char *)));
if( !temp )
{
perror( "malloc failed" );
// perform cleanup and
free( arr );
exit( EXIT_FAILURE );
}
arr = temp;
arr[count-1] = strdup( tok );
if( !arr[count-1] )
{
perror( "strdup failed" );
// perform cleanup and
free( arr );
exit( EXIT_FAILURE );
}
tok = strtok(NULL,delim);
}
*arrsz = count;
return( arr );
}
OP's code does not return the allocated memory assigned to arr
int split(char *str, char **arr) {
...
// Memory allocated and assigned to local `arr`
// Yet `arr` is not returned.
// Calling code never sees the result of this assignment.
arr = malloc(sizeof(char **));
...
return(arrsz);
}
Instead, I took a whole new approach to mimic split /PATTERN/,EXPR.
I really wanted to avoid all the ** and *** programming.
IMO, a split() should not change the expression so directly using strtok() is out. A common implementation of strtok() effectively does a strspn() and strcspsn(), so coding those directly avoids the strtok().
The below returns a string list type. Various other function signatures could be used, this return type seemed natural for OP's goal. Another solution might return a NULL terminated array of char * pointers.
When memory allocations fails, it is detected and then code calls TBD_Code();. Unclear how OP wants to handle that. Code could print a message and exit or attempt some recovery.
#include <stdlib.h>
#include <string.h>
typedef struct {
size_t n;
char **strings;
} string_list;
string_list split(const char *pattern, const char *expr) {
string_list list = { 0, NULL };
size_t length;
// Find length of initial matching characters
while ((length = strspn(expr, pattern)), expr[length]) {
// Skip leading characters from `expr` that match the pattern
expr += length;
// Find length of characters NOT from the pattern
length = strcspn(expr, pattern);
// Allocate for 1 more pointer
void *tmp = realloc(list.strings, sizeof *(list.strings) * (list.n + 1));
if (tmp == NULL) TBD_Code();
list.strings = tmp;
//Allocate for the token and save it
list.strings[list.n] = malloc(length + 1u);
if (list.strings[list.n] == 0) TBD_Code();
memcpy(list.strings[list.n], expr, length);
list.strings[list.n][length] = '\0';
// Advance
list.n++;
expr += length;
}
return list;
}
void string_list_free(string_list list) {
if (list.strings) {
for (size_t i = 0; i < list.n; i++) {
free(list.strings[i]);
}
free(list.strings);
}
}
Test code
#include <stdio.h>
void print_string_list(string_list list) {
for (size_t i = 0; i < list.n; i++) {
printf("%zu: <%s>\n", i, list.strings[i]);
}
string_list_free(list);
}
int main(void) {
print_string_list(split(":", "aaa:bbb:ccc"));
print_string_list(split(":b", "aaa:bbb:ccc"));
print_string_list(split("a:", "aaa:bbb:ccc"));
print_string_list(split(":c", "aaa:bbb:ccc"));
}
Output
0: <aaa>
1: <bbb>
2: <ccc>
0: <aaa>
1: <ccc>
0: <bbb>
1: <ccc>
0: <aaa>
1: <bbb>
In this C program I read in words typed with my keyboard to a char pointer. The pointer is stored in an pointer array. Then I want to sort the array with qsort by the function comparison. I give it the pointer to my pointer array.
It doesnt sort the array at all. I dont know if I got here a UB, or I miss memory by wrong allocation.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
bool read_word(char ***a, int *length);
int comparison(const void *p, const void *q);
int main()
{
int *length = malloc(sizeof(int));
*length = 0;
char **array = malloc(sizeof(char *));
bool go = false;
while(go == false)
{
printf("Enter word: ");
go = read_word(&array,length);
}
qsort(array, *length - 1,sizeof(char *), comparison);
printf("\n");
for(int i = 0; i < *length; i++)
printf("%s\n", array[i]);
return 0;
}
bool read_word(char ***a, int *length)
{
char ch;
++*length;
char *word = malloc(20 * sizeof(char) + 1);
char *keep_word;
char **temp = realloc(*a,*length * sizeof(*a));
if(!temp)
exit(EXIT_FAILURE);
*a = temp;
keep_word = word;
while((ch = getchar()) != '\n')
*keep_word++ = ch;
*keep_word = '\0';
if(word == keep_word)
{
free(word);
--*length;
return true;
}
(*a)[*length - 1] = word;
printf("%s", (*a)[*length -1]);
printf("\nh\n");
return false;
}
int comparison(const void *p, const void *q)
{
const char *p1 = p;
const char *q1 = q;
return strcmp(p1,q1);
}
change to
int length = 0;//no need malloc
char **array = NULL;//first value is NULL
qsort(array, length, sizeof(char *), comparison);//not length-1
int ch;//type of return value of getchar is int
for(int i = 0; i < 20 && (ch = getchar()) != '\n'; ++i)//i : guard
const char *p1 = *(const char **)p;//qsort pass pointer to object to comparison function
const char *q1 = *(const char **)q;//if char *, char **
You do
keep_word = word;
and later
if(word == keep_word)
The condition in brackets is always true.
On a side note, your program is error-prone and hard to understand because you use pointers way too much. In main(), length should be int, not int*, array should be char*, not char** . In the read_word, a should be char**, not char***. Don't use pointers unless necessary!
I'm having an entry level C pointer problem... Let's say I have two strings and I want to print them. What am I misunderstanding in the code below?
void print_array(char **array[]) {
int i = 0;
while((*array)[i] != NULL) {
printf("%s\n", (*array)[i++]);
}
return;
}
int main(int argc, char** argv) {
char str1[] = "hello";
char str2[] = "world";
char **array = {str1, str2};
print_array(&array);
return (EXIT_SUCCESS);
}
For my code to work I need to access the array like it is in print_array
Another take on it (Much more refactoring).
Bugfixes:
Corrected confusion what should be dereferenced and how in print_array (Don't aspire to 3-star-programming, unless you must)
Added the missing sentinel-0 in to the array passed from main to print_array
Other changes:
Removed superfluous return-statements (main has an implicit return 0; at the end)
Removed superfluous checks for unequality to 0 / NULL
Removed one level of indirection from print_array
Use of const in print_array where appropriate
Eliminated counter-variable in print_array
Used a constant compound-literal in main (Needs C99)
#include <stdio.h>
void print_array(const char *const array[]) {
while(*array)
printf("%s\n", *array++);
}
int main() {
print_array((const char*[]){"hello", "world", 0});
}
See here on coliru
Undoing the cleanup steps changing the signature of print_array:
#include <stdio.h>
void print_array(char **array[]) {
for(char** p = *array; *p; p++)
printf("%s\n", *p);
}
int main() {
print_array(&(char**){(char*[]){"hello", "world", 0}});
}
Live on coliru
There's one too many *'s in print_array. It ought to be this:
void print_array(char *array[]) {
int i = 0;
while(array[i] != NULL) {
printf("%s\n", array[i++]);
}
}
That makes calling it straightforward. Change char **array to char *array[]. And don't forget to include a NULL entry at the end of the array.
int main(int argc, char** argv) {
char str1[] = "hello";
char str2[] = "world";
char *array[] = {str1, str2, NULL};
print_array(array);
return EXIT_SUCCESS;
}
On the other hand, if you're not supposed to modify print_array, then that's annoying. You'll need another variable to hold array's address temporarily.
char *array[] = {str1, str2, NULL};
char **p = array;
print_array(&p);
That's why I say there's one too many *'s in print_array.
since you're dealing with arrays, please pass in the number of array members to print_array. The code above is UB.
To fix your code:
void print_array(char *arr[], int cnt) {
int i = 0;
for(i = 0; i < cnt; i++) {
printf("%s\n", arr[i]);
}
return;
}
int main(int argc, char** argv) {
char str1[] = "hello";
char str2[] = "world";
char *arr[] = {str1, str2};
print_array(arr, 2);
return (0);
}
Another way:
#include <stdio.h>
void print_array(char *array[]) {
int i = 0;
while (array[i] != 0) {
printf("%s\n", array[i++]);
}
return;
}
int main(int argc, char** argv) {
char str1[] = "hello";
char str2[] = "world";
char *array[] = {str1, str2, 0};
print_array(array);
return 0;
}