Array of Strings some positions are not printing - arrays

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.

Related

Unexpected problem with Bidimensional char VLA pointer

I did a simple program that splits a string in substrings, using the whitespace as a split reference. The program was working as expected, so I've decided to put this code inside a function that is called "substring_whitespace".This function return a size_t value which is the number of substring's. The function arguments are char* buffer[] and char* string. Both are pointers, the first will store the substring's, and the second is the string that'll be splited.
Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
size_t substring_whitespace(char* buffer[],char* string) {
size_t initial_string_size = strlen(string) + 1;
char actual_string[initial_string_size];
char strings[initial_string_size][initial_string_size];
strcpy(actual_string,string);
size_t c = 0;
for(; c<initial_string_size; c++) {
size_t first_whitespace_index = strcspn(actual_string," ");
char substring[first_whitespace_index];
for(size_t d = 0; d<=first_whitespace_index; d++) {
if(d == first_whitespace_index)
substring[first_whitespace_index] = 0;
else
substring[d] = actual_string[d];
}
size_t actual_string_length = strlen(actual_string);
size_t new_actual_string_length = (actual_string_length - first_whitespace_index) + 1;
char new_actual_string[new_actual_string_length];
for(size_t d = 0,i = first_whitespace_index + 1; i<=actual_string_length + 1; i++,d++) {
if(i == actual_string_length)
new_actual_string[d] = 0;
else
new_actual_string[d] = actual_string[i];
}
strcpy(actual_string,new_actual_string);
strcpy(strings[c],substring);
buffer[c] = strings[c];
if(new_actual_string_length == 1)
break;
}
return ++c;\
}
int main() {
char string[1000];
fgets(string,sizeof(string)/sizeof(string[0]),stdin);
string[strcspn(string,"\n")] = 0;
char* buffer[strlen(string) + 1];
size_t buffer_length = substring_whitespace(buffer,string);
for(int d = 0; d<buffer_length; d++) {
printf("\n%s",buffer[d]);
}
}
After I test, the results were not as expected, so during my debug I detect that the char were being changed after get off the function by pointer. This behavior is only detectable if I try to print the buffer strings in the main.
strings is a local variable whose lifetime ends when the function returns. The easiest fix is to copy the string when assigning a value buffer[c]:
buffer[c] = strdup(strings[c]);
Another option is to change the design and return an array of ranges relative to your input string. For example struct range { char *s; size_t len; };, and if string is "hello world" the function could return [{string, 5}, {string+6, 5}].

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

How to concatenate two string array index by index in C?

I have two arrays of strings called name and subject. I want to have another array of strings whose elements are obtained by concatenating the string of the first array with the string with the same index of the other array. The new array should be the output of a function.
Here I give a code sample, but I am unable to compile due to getting errors.
I have also seen this question but I am unable to use it.
Can anyone give me a hint on how to solve this without dynamic allocation and also with dynamic allocation?
#include <stdio.h>
#include <string.h>
const int MAX = 4;
char* concancate_string(char* name,char* subject);
int main () {
char* name[] = {
"michel",
"sam",
"roy",
"romi"
};
char* subject[] = {
"physics",
"math",
"chemistry",
"biology"
};
char* p[];
p=concancate_string(name,subject);
for ( int i = 0; i < MAX; i++) {
printf("name and subject[%d] = %s\n", i, name[i] );
}
return 0;
}
char* concancate_string(char* name,char* subject)
{
for ( int i = 0; i < MAX; i++) {
strcat(name[i]," : ");
strcat(name[i],subject[i]);
}
return name;
}
resulted output array:
{
"michel : physics",
"sam : math",
"roy : chemistry",
"romi : biology"
}
Here's my attempt with dynamic allocation:
char **concancate_string(const char *name[], const char *subject[], size_t n) {
char **destin = malloc(n * sizeof *destin);
for (int i = 0; i < n; i++) {
destin[i] = malloc(strlen(name[i]) + strlen(subject[i]) + 3 + 1); // add space for " : " and terminating '\0'
sprintf(destin[i], "%s : %s", name[i], subject[i]);
}
return destin;
}
Remember to all free(destin[k]) and free(destin).
See code running on https://ideone.com/3Qb7v1
First of all, this declaration doesn't work:
char* p[]; // how much stack memory should the compiler reserve?
p=concancate_string(name,subject); // can't assign to an array
Instead, do this:
char **p = concancate_string(name, subject); // you can assign to pointers, though
Also this signature is wrong:
char* concancate_string(char* name,char* subject);
It's taking and returning arrays of char*, not single char*, so it should be:
char **concancate_string(char **name, char **subject);
Furthermore, you can't concatenate to a pointer that you assigned a string literal to. Those point to your program's binary, which is readonly. Instead, the function should look like this:
char **concancate_string(char **name, char **subject)
{
char **pointers = malloc(MAX * sizeof(char*));
for (int i = 0; i < MAX; i++) {
pointers[i] = malloc(strlen(name[i]) + strlen(subject[i]) + 4);
sprintf(pointers[i], "%s : %s", name[i], subject[i]);
}
return pointers;
}
Note how we're allocating an array for the pointers, then allocate memory for every single string, then use sprintf to assemble them (you could also use strcpy and strcat, of course).
Finally, your print is wrong. You make your p, but instead of printing that, you print name. It should instead be:
printf("name and subject[%d] = %s\n", i, p[i]);
And when you're done, the memory should be freed:
for (int i = 0; i < MAX; i++) {
free(p[i]);
}
free(p);
My suggestion to you is to write your programs one part of the time, only starting with the next part when the last part is tested and works well. If you just write the entire program without testing and then it doesn't work because there's errors all over the place, it becomes much harder to find them.
If you can assume a maximum length of each string then there is no need to use dynamic allocation. In the example below (which compiles and run) I assumed each string has a length of 100 (99 usable characters plus the \0 character).
So I defined an array using your MAX constant and 100 as char result[MAX][100] = {0};. {0} initializes all the elements to 0 (this initialization works only with 0. Then I passed this new array to the function. Note that you were defining the function parameter as char* name which means a string: you want to pass an array of strings: I redefined as concancate_string(char* name[], char* subject[], char out[MAX][100]): note the difference.
Strings are simply concatenated with strcat. There is also another function strncat which allows you to specify the max number of char to copy.
#include <stdio.h>
#include <string.h>
const int MAX = 4;
int concancate_string(char* name[], char* subject[], char out[MAX][100]);
int main () {
char result[MAX][100] = {0} ;
char* name[] = {
"michel",
"sam",
"roy",
"romi"
};
char* subject[] = {
"physics",
"math",
"chemistry",
"biology"
};
int p=concancate_string(name, subject, result);
for ( int i = 0; i < MAX; i++) {
printf("%s\n", result[i] );
}
return 0;
}
int concancate_string(char* name[], char* subject[], char out[MAX][100])
{
for ( int i = 0; i < MAX; i++) {
strcat(out[i], name[i]);
//printf("%s\n", out[i] );
strcat(out[i], " : ");
//printf("%s\n", out[i] );
strcat(out[i], subject[i]);
//printf("%s\n", out[i] );
}
retur

Counting # of index of undefined char array in C

I'm trying to count the number of indexes of an undefined char array which is used as a parameter in the function.
I am already aware that if my array was fixed I can use "sizeof", which isn't the case here.
Attempt:
int counting(char *name3) {
int count = 0;
int i;
//I have no idea what to put as my condition nor do I believe
//I am approaching this situation correctly...
for (i = 0; i < sizeof(name3); i++) {
if (name3[i] != '\0') {
count++;
}
}
return count;
}
Then if it is run by the following code
int main(void) {
char *name = "Lovely";
int x = counting(name);
printf ("The value of x = %d", x);
Prints: The value of x = 0
Any help or pointers would be amazing. Thank you in advance.
In C, Every string ends with '\0' (Null Character)
You can iterate until you meet the Null Character
The example code would be like this
char* name = "Some Name";
int len = 0;
while (name[len] != '\0') {
len++;
}
Also, if it is a char pointer, not char array, sizeof(char*) will always return 4 in 32-bit application and return 8 in 64-bit application (the size of the 'pointer' itself - the memory address size)
#include <stdio.h>
int main()
{
int i=0;
char *name = "pritesh";
for(i=0;;i++)
{
if(name[i] == '\0')
{
break;
}
}
printf("%d", i);
return 0;
}
This should work
note: this might be syntactically incorrect as I have not had my hands on c since a long time

A simple C exercise going wrong: Trying to return a pointer to an array of two strings

"Return a pointer to an array of two strings. The first is the characters
of string s that are at even indices and the second is the characters from
s that are at odd indices"
char **parity_strings(const char *s) {
char** parity = malloc(sizeof(char*) * 2);
char even_strings[] = "";
char odd_strings[] = "";
int x = 0;
int y = 0;
for (int i = 0; i < strlen(s); i++) {
if ((i % 2) == 0) {
even_strings[x] = s[i];
x++;
}
else {
odd_strings[y] = s[i];
y++;
}
}
parity[0] = even_strings;
parity[1] = odd_strings;
return parity;
}
int main(int argc, char **argv) {
char **r = parity_strings(argv[1]);
printf("%s %s %s", r[0], r[1], argv[1]);
return 0;
}
My logic makes sense but the output is always incorrect. For example, with input ababab I get back ababab while the expected output is aaa bbb ababab. What did I do wrong?
The string named even_strings is a local variable, so its memory will be freed after your function returns, so it is not valid to try to return a pointer to it to the caller.
Try changing this line:
char even_strings[] = "";
to something like this:
char * even_strings = malloc(some_size);
The same goes for your odd_strings string.
Also, be sure to pick a good value for some_size so that your program allocates enough memory for each string so that it can hold all the data you are writing to it.
even_strings and odd_strings are arrays of size 1 each. Your code writes out of bounds (even_strings[x] = s[i], odd_strings[y] = s[i]). Furthermore, they're local variables that cease to exist once parity_strings returns, so the returned pointers are garbage.

Resources