C function doesn't modify passed array - arrays

Hi I need help with my C assignment. I need to modify a dynamic 2d array, so I pass a pointer to it to function, unfortunately it seems to not modify it but only copy it and leave it untouched, I have tried everything, can someone help please?
int getUniqueRelMembersCount(universe_t u, relation_t rel, char **members)
{
int memberCount = 0;
//count and dynamically populate passed 2d array with unique members of rel
return memberCount;
}
How I call the function:
char **members = malloc(sizeof(members));
int len = getUniqueRelMembersCount(universe, relations.relations[0], members);
printf("%d\n", len);
printf("%s", members[0]);
for (int i = 0; i < len; i++)
{
free(members[i]);
}
free(members);
output:
3
Segmentation fault
EDIT:
Ok, I tried to make a demo using your answer and I am still confused:
int populate(char ***members)
{
*members = malloc(sizeof(*members));
for (int i = 0; i < 3; i++)
{
char string[6] = {'H', 'e', 'l', 'l', 'o', '\0'};
*members = realloc(*members, (i + 1) * sizeof(char *));
*members[i] = malloc(strlen(string) * sizeof(char));
strcpy(*members[i], string);
}
return 0;
}
int main()
{
char **members;
populate(&members);
printf("%s\n", members[0]);
printf("%s\n", members[1]);
printf("%s\n", members[2]);
return 0;
}
output:
Hello
đOu
(null)

It does modify the array. It might even free it. That's not the problem.
The problem is it also changes the pointer (members = realloc(...)), but you don't propagate this change to the caller. Despite having the same name, the variable named members in your function and the variable named members in the caller are different variables. After your realloc, the members variable in main might no longer be valid.
Simplified demo:
void f(int members) {
printf("%d\n", members); // 2
members = 3;
printf("%d\n", members); // 3
}
int main(void) {
int members = 2;
printf("%d\n", members); // 2
f(members);
printf("%d\n", members); // 2
return 0;
}
You could return the modified value. In this case, it's better to pass a pointer to the variable.
int f(int *membersPtr) {
printf("%d\n", *membersPtr); // 2
*membersPtr = 3;
printf("%d\n", *membersPtr); // 3
}
int main(void) {
int members = 2;
printf("%d\n", members); // 2
f(&members);
printf("%d\n", members); // 3
return 0;
}
In your full program, a pointer to the variable would have type char***.

Your following code shouldn't work:
char **members = malloc(sizeof(members));
int len = getUniqueRelMembersCount(universe, relations.relations[0], members);
because:
you cannot use 'member' value to initialize 'member';
malloc returns signle pointer, but not double pointer.
Try this:
int members_count = 10;
char *members = malloc(members_count);
int len = getUniqueRelMembersCount(universe, relations.relations[0], &members);

Related

GCC Compilation Error on array assignment

I am trying to convert a string into its equivalent matrix form in C. The matrix would have 3 rows and as many columns as required. The following code doesn't compile, and I haven't figured out what's going wrong.
The error that GCC throws is:
app.c:10:25: error: subscripted value is not an array, pointer, or vector
printf("%d\n", arr[i][k]);
~~~^~
1 error generated.
Main file (app.c):
#include <stdio.h>
#include "converter.h"
int main() {
char source[] = "This is the source. "; // placeholder text
int arr = convert(source);
for (int i = 0; i < 21; i++) {
for (int k = 0; k < 3; k++) {
printf("%d\n", arr[i][k]); // error occurs at this line.
}
}
return 0;
}
converter.c file:
// Converts an input string to its respective ASCII matrix.
#include <string.h>
#include <stdio.h>
#include "converter.h"
// Converts the entire string into an multi-dimensional array.
int convert(char text[]){
// copy the input text into a local store.
char store[strlen(text)];
strcpy(store, text);
// make sure the length of the input string is a multiple of 3 or make it so.
int excess = strlen(store)%3;
char excess_spaces[3] = " ";
if (excess != 0) {
strncat(store, excess_spaces, 3-excess);
}
// covert the source into an array
int arr[3][strlen(store)/3];
int steps = strlen(store)/3;
for (int i = 0; i < steps; i++) {
int t[3];
for (int k = 0; k < 3; k++) {
t[k] = (int) store[3*i+k];
arr[k][i] = t[k];
}
}
return arr;
}
converter.h file:
int convert(char text[]);
There are multiple issues in this code.
The allocating storage for string, one must include one byte for a null terminator. Replace:
char store[strlen(text)];
with:
char store[strlen(text) + 1];
Additionally store must be big enough to contain the excess which is up to 3 spaces.
char store[strlen(text) + 3 + 1];
In C you cannot use an array as a value. It is converted to a pointer to it's first element in pretty must every context. Therefore it is not possible to return an array directly. It could be workaround by wrapping an array with a struct but it a topic for another day.
As result return arr will be equivalent to return &arr[0] which is int (*)[XXX] a pointer to int array of size XXX.
Never ever return a pointer to an object with automatic storage. It's Undefined Behaviour. I know that the intention was returning an array not a pointer to it. Create an object with dynamic storage with malloc-like function to safely return a pointer.
Returning Variable Length Array (VLA) by value is not possible because Variably Modified (VM) types cannot be defined at file scope.
It looks that indices are swapped in:
printf("%d\n", arr[i][k]);
I guess it should be arr[k][i].
Now... let's solve it.
Returning VLA is tricky. One solution is to pass a pointer to VLA as an argument. See https://stackoverflow.com/a/14088851/4989451.
The issue with this solution is that the caller must be able to compute the dimensions.
The other way it to wrap the result of the convert() to a struct. Note that the function and the struct can share the name. The result with have the sizes of VLA as n and m members and the pointer to the data as arr. The caller need to cast it to proper VM type.
To cumbersome casts between the non-trivial pointer types, one can cast via void*.
When all work with the array is done, release it memory with free().
// Converts an input string to its respective ASCII matrix.
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
// Converts the entire string into an multi-dimensional array.
struct convert {
int n, m;
int *arr;
} convert(char text[]){
// copy the input text into a local store.
size_t textlen = strlen(text);
char store[textlen + 3 + 1];
strcpy(store, text);
// make sure the length of the input string is a multiple of 3 or make it so.
int excess = textlen % 3;
char excess_spaces[3] = " ";
if (excess != 0) {
strncat(store, excess_spaces, 3-excess);
}
size_t storelen = strlen(store);
// allocate VLA with dynamic storage
int (*arr)[storelen / 3] = malloc(3 * sizeof *arr);
// covert the source into an array
int steps = storelen / 3;
for (int i = 0; i < steps; i++) {
int t[3];
for (int k = 0; k < 3; k++) {
t[k] = (int) store[3*i+k];
arr[k][i] = t[k];
}
}
return (struct convert){ .n = 3, .m = steps, .arr = (int*)arr };
}
int main() {
char source[] = "This is the source. "; // placeholder text
struct convert res = convert(source);
int n = res.n, m = res.m;
int (*arr)[m] = (void*)res.arr;
for (int i = 0; i < n; i++, puts("")) {
for (int k = 0; k < m; k++) {
printf("%d ", arr[i][k]); // error occurs at this line.
}
}
free(arr);
return 0;
}

Array of Strings some positions are not printing

So, I have an array of strings and the first two positions are not printing, I'm pretty sure it's a size problem, but I don't understand why
#include <stdio.h>
char * switch(int i) {
char letters[8][20] = {"Caio\0", "Eduarda\0", "Joanderson\0", "Heron\0", "Thiago\0", "Rafaela\0", "Thalisson\0", "Wilton\0"};
if ((i >=0) && (i<=7)) {
char*str = letters[i];
return (str);
} else {
return "-";
}
}
int main()
{
for(int i = 0; i < 8; i++){
printf("%d -- %s \n", i, switch(i));
}
return 0;
}
There are two ways to solve the problem:
Using dynamic memory allocation:
#include <stdio.h>
#include <stdlib.h>
char *switch1(int i)
{
char **letters = malloc(8 * sizeof(char *));
for (int i = 0; i < 8; i++)
{
letters[i] = malloc(20 * sizeof(char));
}
letters[0] = "Caio";
letters[1] = "Eduarda";
letters[2] = "Joanderson";
letters[3] = "Heron";
letters[4] = "Thiago";
letters[5] = "Rafaela";
letters[6] = "Thalisson";
letters[7] = "Wilton";
if ((i >= 0) && (i <= 7))
{
char *str = letters[i];
return (str);
}
else
{
return "-";
}
}
int main()
{
for (int i = 0; i < 8; i++)
{
printf("%d -- %s \n", i, switch1(i));
}
return 0;
}
Using static memory allocation :
#include <stdio.h>
char *switch1(int i)
{
static char letters[8][20] = {"Caio", "Eduarda", "Joanderson","Heron","Thiago", "Rafaela", "Thalisson", "Wilton"};
if ((i >= 0) && (i <= 7))
{
char *str = letters[i];
return (str);
}
else
{
return "-";
}
}
int main()
{
for (int i = 0; i < 8; i++)
{
printf("%d -- %s \n", i, switch1(i));
}
return 0;
}
I got an error, if I use switch as the name of the function. switch is keyword in C.
Lifetime of letters ends at the end of the switch1 function. We have to use static or dynamic memory allocation to extend its lifetime to entire program.
char letters[8][20] = …; defines an array with automatic storage duration. Memory is reserved for it only during the function call. The statement return (str); returns a pointer to an element of this array, and then the memory is no longer reserved for the array. In C’s abstract model of computing, the array ceases to exist. In typical practice, the array is on the stack, but the printf call overwrites it.
You can fix this by defining the array with static storage duration, which reserves memory for it during all of program execution:
static char letters[8][20] = {"Caio\0", "Eduarda\0", "Joanderson\0", "Heron\0", "Thiago\0", "Rafaela\0", "Thalisson\0", "Wilton\0"};
Additionally, it is not necessary to include a null character at the end of a quoted string. String literals automatically include a null character at the end.
Also, string literals define arrays with static storage duration themselves. So, instead of using the strings to initialize an array, you could instead point directly to the strings:
char *letters[8] = {"Caio", "Eduarda", "Joanderson", "Heron", "Thiago", "Rafaela", "Thalisson", "Wilton"};
That array could also be made static. It will work either way (because your function will not return a pointer to an element in the array but will return a pointer, taken from the array, that points to a string with static storage duration), but, if it is made static, it will not have to be initialized every time the function is called.

Except the return value, does a variable's address must be sent to a function to modify its value in C?

I try to understand when a variable's value gets modified by passing through a function in C.
I know that in C, there's two ways to change variable's value :
Using the return value of a function
Passing a variable's address to modify its content
Here's the code :
// by address
void foo(int *nb)
{
*nb = 10;
}
int main(void)
{
int nb = 5;
foo(&nb);
printf("%i\n", *nb); // It prints 10
}
// Code to explain
void foo(char **tab)
{
tab[2] = "44";
}
void bar(char *str)
{
str[1] = 'a';
}
int main(void)
{
char **tmp = malloc(sizeof(char *) * 4);
char *str = strdup("Hello");
for (int i = 0; i < 3; ++i)
tmp[i] = malloc(3);
tmp[0] = "11";
tmp[1] = "22";
tmp[2] = "33";
tmp[3] = NULL;
foo(tmp); // It modifies tmp's value
bar(str); // It modifies str's value
for (int i = 0; i < 3; ++i)
printf("%s\n", tmp[i]);
printf("%s\n", str);
}
Output:
11
22
44
Hallo
Expected:
11
22
33
Hello
I was expecting to send a copy to the function but at the end, both string and char ** are modified.
Why variables are modified here ?
Your comment on foo(tmp) says “It modifies tmp's value,” but this is not correct. tmp is a pointer, and the value of the pointer is not modified by foo. The call foo(tmp) passes the value of tmp to foo, and then foo modifies the things that are pointed to. It changes tmp[2] to point to "44". tmp[2] is one of the things pointed to by tmp; it is not tmp.
Similarly, in bar(str), str is a pointer, and the value of the pointer is not changed by var. Rather, bar changes one of the characters in the string that str points to.

C pass array to a function by pointer

I am new to C and pointers and I'd like know if its possible to pass an array pointer to a function instead of passing the array of characters itself. I am posting the snippet from the code.
char ipAddress[24];
int i, j;
for (i = 12; i <= 13; i++)
{
for (j = 1; j <= 254; j++)
{
sprintf(ipAddress,"192.168.%d.%d",i,j);
runCommand(ipAddress);
}
}
// ...
int runCommand (char x[24])
{
// Do stuff.
}
Arrays are always passed by pointer in C, not passed by value (copyed)
So
int runCommand (char x[24]);
is close equivalent of
int runCommand (char *x);
Yes, it is possible to pass a pointer to an array to a function. No, it is probably not what you want.
int runCommand(char (*x)[24])
{
if ((*x)[0] == '\0') // Option 1
return -1;
if (x[0][0] == '\0') // Option 2: equivalent to option 1.
return -1;
...
}
void alternative(void)
{
char y[24] = "Samizdat";
printf("%d\n", runCommand(&y));
}
That says x is a pointer to an array of 24 characters. Be very careful, though. In general, you do not want to pass a pointer to an array; you just want to pass pointers around.
int runCommand(char x[24]) // Or: char *x
{
if (x[0] == '\0') // Option 1
return -1;
...
}
void alternative(void)
{
char y[24] = "Samizdat";
printf("%d\n", runCommand(y));
}

C: Copy array without knowing its type

I have function, it receive pointer to array and pointer to function and should return new array with order defined by function passed in parameter.
My problem how I copy element of one array to another without knowing its type
void * scrambleArr(void * arr, int numElem, int elemSize, int (*func)(void*)) {
void * newArr;
int cPos, newPos,i;
newArr = (void *)malloc(numElem*elemSize);
for (i=0 ; i < numElem ; i++)
{
cPos = i*elemSize;
newPos = func((char*)arr+cPos);
*((char*)newArr+newPos) = *((char*)arr+cPos);
}
return newArr;
}
Function that passed in the last parameter
int posArrayBySize(void *el) {
ARRAY* arr = (ARRAY *)el;
return arr->size - 1;
}
And code in main:
int main( ) {
ARRAY * arrSorted;
int a[2] = {1,2};
int b[3] = {1,1,1};
int c[1] = {9};
int d[4] = {3,3,3,3};
ARRAY arr[4] = {{a,2},{b,3},{c,1},{d,4}};
arrSorted =(ARRAY *)scrambleArr(arr,4,sizeof(ARRAY),posArrayBySize);
free(arrSorted);
return 0;
}
After running arrSorted contain garbage,
Can someone point me, what i miss?
Another option for me is not to copy, just to point one array to elements of other, is it possible?
Thanks.
memcpy is the function you are looking for.
This won't work
*((char*)newArr+newPos) = *((char*)arr+cPos);
because you're dereferencing arr+cPos as it is char, so it will copy only the first byte.

Resources