How to properly free malloced variable passed into variadic function? - c

Consider the following example
char** make_copy(int argc, char** argv){
char** new_argv = malloc((argc+1) * sizeof *new_argv);
for(int i = 0; i < argc; ++i)
{
size_t length = strlen(argv[i])+1;
new_argv[i] = malloc(length);
memcpy(new_argv[i], argv[i], length);
}
new_argv[argc] = NULL;
return new_argv;
}
Above function malloc memory to make copies
void free_space(**args, ...){
// how to do ?
}
int main(){
char** string1 = make_copy(); # make a copy of some string
char** string2 = make_copy(); # make a copy of another string
char** string3 = make_copy(); # make a copy of third string
free_space(string1, string2, string3, NULL);
}
without make any assumption of number of passing variable, what is the most efficient way of freeing malloced space?

A macro can send a dummy first parameter and a final NULL, then just use a va_list inside your function:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#define free_all(...) free_all(0, __VA_ARGS__, NULL)
void (free_all)(int dummy, ...)
{
va_list args;
va_start(args, dummy);
while (1)
{
char **ptr = va_arg(args, char **);
if (ptr == NULL)
{
break;
}
while (*ptr != NULL)
{
free(*ptr);
ptr++;
}
}
va_end(args);
}
int main(void)
{
char *a[] = {strdup("abc"), strdup("123"), NULL};
char *b[] = {strdup("bcd"), strdup("456"), NULL};
char *c[] = {strdup("cde"), strdup("789"), NULL};
free_all(a, b, c);
}
or if your prefer you can pass a compound literal to a function expecting an array of pointers (with the advantage that the compiler can do type checking):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void free_all(char **list[])
{
while (*list != NULL)
{
char **ptr = *list;
while (*ptr != NULL)
{
free(*ptr);
ptr++;
}
list++;
}
}
int main(void)
{
char *a[] = {strdup("abc"), strdup("123"), NULL};
char *b[] = {strdup("bcd"), strdup("456"), NULL};
char *c[] = {strdup("cde"), strdup("789"), NULL};
free_all((char **[]){a, b, c, NULL});
return 0;
}
if you want to hide the construction of the compound literal at the cost of obfuscating the code with a macro:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define free_all(...) free_all(((char **[]){__VA_ARGS__, NULL}))
void (free_all)(char **list[])
{
while (*list != NULL)
{
char **ptr = *list;
while (*ptr != NULL)
{
free(*ptr);
ptr++;
}
list++;
}
}
int main(void)
{
char *a[] = {strdup("abc"), strdup("123"), NULL};
char *b[] = {strdup("bcd"), strdup("456"), NULL};
char *c[] = {strdup("cde"), strdup("789"), NULL};
free_all(a, b, c);
return 0;
}

What you want is to use a sentinel value to mark the end of the variable arguments list:
void free_one(void *ptr) {
// logic to free a pointer
}
void free_all(void *ptr, ...) {
// short-circuit free_all(NULL)
if (!ptr) return;
va_list args;
free_one(ptr);
va_start(args, ptr);
while (true) {
void *arg = va_arg(args, void *);
// stop on the first NULL
if (!arg) break;
free_one(arg);
}
va_end(args);
}
Usage:
free_all(NULL); // no-op
free_all(string1, string2, string2, NULL);
The only caveat is that you can't pass a NULL pointer this way, since NULL denotes the end of the variable arguments list. So:
free_all(string1, NULL, string2);
Will not free string2.

You have to iterate over arguments and free them one by one. I would do just:
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
char** make_copy(int argc, char** argv) {
char** new_argv = malloc((argc + 1) * sizeof *new_argv);
for (int i = 0; i < argc; ++i) {
size_t length = strlen(argv[i]) + 1;
new_argv[i] = malloc(length);
memcpy(new_argv[i], argv[i], length);
}
new_argv[argc] = NULL;
return new_argv;
}
void free_one(char** ptr) {
for (char** i = ptr; *i; ++i) {
free(*i);
}
free(ptr);
}
void free_space(char** ptr, ...) {
va_list va;
va_start(va, ptr);
while (ptr) {
free_one(ptr);
ptr = va_arg(va, char**);
}
va_end(va);
}
int main() {
char** string1 = make_copy(1, (char*[]){"1a"});
char** string2 = make_copy(2, (char*[]){"2a", "2b"});
char** string3 = make_copy(3, (char*[]){"3a", "3b", "3c"});
free_space(string1, string2, string3, NULL);
}

Related

In C, how can one dynamically build an array of strings in a function and return to caller

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>

Problems with arrays of pointers

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;
}

How to check if a pointer has been freed

I am a beginner in C. Below is my scenario - I have created a pointer variable in main function and it has been passed on to several functions(in this example 2 levels). And one of the functions frees it up. Now I need to have check in Main to see whether the pointer is freed or not, that means i need to setup the value of &str in main() to point to NULL. Not sure my approach is right here. Any help would be much appreciated
void func2(char *str)
{
free(str);
}
void func1(char *str)
{
func2(str);
}
int main()
{
char *str;
str=(char *) malloc(10);
func1(str);
if(str){ do something; } // if condition to check whether str is freed
}
#include <stdio.h>
#include <stdlib.h>
func2(char **str)
{
free(*str); //free
*str = NULL; //Set to NULL
}
func1(char **str) //func1 receives as **
{
func2(str); //Pass pointer to func2()
}
int main()
{
char *str = NULL;
str=(char *) malloc(10);
func1(&str); //Pass Address of pointer to func1()
if(str) //Check for NULL
{
printf("\n Not - Freed...\n");
}
else
{
printf("\n Freed...\n");
}
return 0;
}
In C all are pass by value. I suggest to study http://www.cs.fsu.edu/~myers/cgs4406/notes/pointers.html for understanding of this.
You could try something like this - first redefine malloc and free (track.h)
#ifndef track_h
#define track_h
extern void* trackmalloc(size_t size);
extern void trackfree(void* array);
extern void trackismalloc(void* array);
#define malloc trackmalloc
#define free trackfree
#endif
Then for every piece of code that uses malloc and free, replace #include with #include "track.h"
#include <stdlib.h>
#include <stdio.h>
#include "track.h" /* was <malloc.h> */
// A function which has a 20% chance of freeing the pointer
void twentypercent(char* array)
{
if (rand() < (RAND_MAX / 5))
free(array);
}
int main(int argc, char* argv[])
{
char* list = malloc(256);
int ii;
for (ii = 0; ii < 10; ++ii)
twentypercent(list);
if (trackismalloc(list)
printf("Not freed yet");
return 0;
}
Now define track.c. This will only free memory that has been allocated by by trackmalloc. If it was not allocated by trackmalloc, then it will report that the memory has already been freed.
#include <stdio.h>
#include <malloc.h>
#define TRACKER_MAX 2048
static void* tracker[TRACKER_MAX] = { 0 };
static int track_last = -1;
void* trackmalloc(size_t size)
{
// For simplicity, tracker will not be reused
tracker[++track_last] = malloc(size);
return tracker[track_last];
}
void trackfree(void* array)
{
// This will slow down as the list gets filled up.
// You will need a more efficient way of searching lists (possibly bsearch)
int tt;
for (tt = 0; tt < track_last; ++tt)
{
if (array == tracker[tt])
{
free(tracker[tt]);
tracker[tt] = 0;
break;
}
}
if (tt == track_last)
printf("%p already freed\n", array);
}
int trackismalloc(void* array)
{
// This will slow down as the list gets filled up.
// You will need a more efficient way of searching lists (possibly bsearch)
int tt, result = 0;
for (tt = 0; tt < track_last; ++tt)
{
if (array == tracker[tt])
{
result = 1;
break;
}
}
return result;
}
void func1(char** str) {
free(*str);
*str = NULL;
}
void func2(char** str) {
free(*str);
*str = NULL;
}
int main() {
char *str;
str = (char*) malloc(10);
func1(&str);
if (str) {
do something;
}
}
void func2(char **str)
{
free(*str);
*str = 0;
}
void func1(char **str)
{
func2(str);
}
int main()
{
char *str;
// I'd recommend using sizeof(type_you_want) * amount_of_elements instead of
// a constant number: -> malloc(sizeof(char) * 10);
str=(char *) malloc(10);
func1(&str); // You must pass the address of the pointer, because you want
// to change "WHAT IT POINTS TO", not "WHAT IS POINTED BY IT"
if(str){ do something; } // if condition to check whether str is freed
}
When you call a function in C, you pass a copy of those arguments, so you are passing a copy of that pointer (that copy still points to the same place, so you can change that place that it points to) but you want to change the pointer value, so you need to pass its address.
I have explained a little bit how pointers inside functions can be used in here
#include <stdio.h>
#include <stdlib.h>
void func2(char **str)
{
printf("%d %s\n",__LINE__,__func__);
free(*str);
*str = NULL;
}
void func1(char **str)
{
printf("%d %s\n",__LINE__,__func__);
func2(str);
}
char * allocaMem(char **ptr)
{
*ptr=(char *) malloc(sizeof(char)* 10);
if(!*ptr)
{
perror("");
}
else
{
return *ptr;
}
}
int main()
{
char *str = allocaMem(&str);
if (!str) {
printf("Error in malloc()\n");
return -1;
}
func1(&str);
if (str) {
printf("Memory Not freed\n");
} else {
printf("Memory freed\n");
}
}

How to use qsort for an array of strings?

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int sortstring(const void *str1, const void *str2) {
const char *rec1 = str1;
const char *rec2 = str2;
}
void sortutil(char* lines[]) {
qsort(lines, 200, sizeof(char), sortstring);
}
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "sortutil.h"
int getarray(char *lines[]) {
int i = 0;
char *text = (char *)malloc(200);
while (fgets(text, 200, stdin) != NULL) {
lines[i] = text;
i++;
text = (char *)malloc(200);
}
return i;
}
void printarray(char *lines[], int max) {
for (int i = 0; i < max; i++)
printf("%s\n\n", lines[i]);
}
int main(int argc, char* argv[]) {
char* arr[100];
int numlines = getarray(arr);
printf("There are %d lines\n", numlines);
printarray(arr, numlines);
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "-s") == 0) {
sortutil(arr);
printarray(arr, numlines);
}
}
}
When I send in a file with arbitrary text, It'll read the file and print it out, but when i call -s and call the qsort function, it comes back with nulls. I'm sure I am using qsort incorrectly, what is the right way to use it for an array to char pointers?
Your comparator is being sent each pair by-address. I.e. they're pointer-to-pointer-to-char.
Change the comparator to:
int sortstring( const void *str1, const void *str2 )
{
char *const *pp1 = str1;
char *const *pp2 = str2;
return strcmp(*pp1, *pp2);
}
Likewise, your sortutil needs to know the number of items being sorted, as well as pass the correct size of each item. Change that to:
void sortutil(char* lines[], int count)
{
qsort(lines, count, sizeof(*lines), sortstring);
}
Finally, the call from main() should look like this:
sortutil(arr, numlines);
That should do it.
What the compar function gets are pointers to the elements in your array, which in this case, are pointers to char. So the parameters str1 and str2 are actually pointers to pointers to char. You must cast them like this:
int sortstring( const void *str1, const void *str2 )
{
const char *rec1 = *(char**)str1;
const char *rec2 = *(char**)str2;
int val = strcmp(rec1, rec2);
return val;
}
Then you have to use the proper element size in qsort.
qsort(lines, 200, sizeof(char*), sortstring);
This line is incorrect.
qsort(lines, 200, sizeof(char), sortstring);
Change it to
qsort(lines, 200, sizeof(char*), sortstring);

C - Not getting the right value with an argument pointer

The get_current_path function gets a pointer to a char string of the current working directory. printf("%s\n", buf); in the function itself prints exactly what I want, but then outside of the function, printf("%s", thisbuf); gives me a lot of garbage. I assume I've made some silly mistake here, but I can't figure out what it is.
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
int get_current_path(char *buf) {
long cwd_size;
char *ptr;
cwd_size = pathconf(".", _PC_PATH_MAX);
if ((buf = (char *) malloc((size_t) cwd_size)) != NULL)
ptr = getcwd(buf, (size_t)cwd_size);
else cwd_size == -1;
printf("%s\n", buf);
printf("%ld\n", cwd_size);
return cwd_size;
}
int main (int argc, char **argv)
{
char *thisbuf;
get_current_path(thisbuf);
printf("%s", thisbuf);
return 0;
}
You should pass a pointer to char *
int get_current_path(char **buf)
{
*buf = ...;
}
int main()
{
char *thisbuf;
get_current_path(&thisbuf);
}
Parameters in C are pass-by-value, which means that get_current_path can't change the value of "thisbuf" passed in by the caller.
To make the change, you would have to pass in a pointer to "thisbuf":
int get_current_path(char **resultBuf) {
char *buf = (char *) malloc((size_t) cwd_size);
...
*resultBuf = buf; // changes "thisbuf" in the caller
}
....
get_current_path(&thisbuf); // note - passing pointer to "thisbuf"
Try this instead:
int get_current_path(char **buf) {
*buf = something; // Set buf with indirection now.
And:
int main (int argc, char **argv)
{
char *thisbuf;
get_current_path(&thisbuf);
printf("%s", thisbuf);
return 0;
}
You were trying to pass a copy of buf to get_current_path, so when buf was modified, the original pointer to buf was not modified.

Resources