I need to create an array of strings, each representing a card of the Spanish deck:
#define __USE_MINGW_ANSI_STDIO 1
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(void)
{
char *type[4]= {"copas", "basto", "espada", "oro"};
char *number[10]= {"Uno", "Dos", "Tres", "Cuatro", "Cinco", "Seis", "Siete", "Diez", "Once", "Doce"};
char *deck[40];
int deckIndex= 0;
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 10; j++) {
char card[100] = "";
strcat(card, number[j]);
strcat(card, " de ");
strcat(card, type[i]);
strcat(card, "\n");
deck[deckIndex]= card;
deckIndex++;
}
}
for (int i = 0; i < 40; i++)
{
printf("%s\n", deck[i]);
}
return 0;
}
However, all entries of deck[] point to the same string. As a result, "Doce de oro" is printed 40 times. I don't understand why this happens, but I've theorized it's because card[] is being reinitialized in the same memory direction, and overrides what was already written there in the previous iteration. If I'm right, I would have to declare every array separately, but I have no idea how to do that without writing 40 different arrays.
Tldr:
¿Why do all entries of deck[] point to the same location?
¿How do I fix it?
(Btw suggestions for a better title are appreciated)
In C, memory on the stack is allocated in terms of Scopes. So yes, your theory is right. You are rewriting on the same location.
To fix your program, there are two possible solutions I can think of.
You can use Multidimensional Arrays.
Or you can allocate memory in heap using malloc (but make sure to free it once you are done with it)
As pointed out in the comments, in the deck[deckIndex]= card; line, you are assigning the same pointer1 to each of your deck elements – and, worse, a pointer to a variable (the card array) that is no longer valid when the initial nested for loop goes out of scope.
To fix this, you can make copies of the card string, using the strdup function, and assign the addresses of those copies to the deck elements. Further, as also mentioned in the comments, you can simplify the construction of the card string using a single call to sprintf, rather than using multiple strcat calls.
Here's how you might do that:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(void)
{
char* type[4] = { "copas", "basto", "espada", "oro" };
char* number[10] = { "Uno", "Dos", "Tres", "Cuatro", "Cinco", "Seis", "Siete", "Diez", "Once", "Doce" };
char* deck[40];
int deckIndex = 0;
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 10; j++) {
char card[100] = "";
sprintf(card, "%s de %s", number[j], type[i]);
deck[deckIndex] = strdup(card);
deckIndex++;
}
}
for (int i = 0; i < 40; i++) {
printf("%s\n", deck[i]);
}
// When you're done, be sure to free the allocated memory:
for (int i = 0; i < 40; i++) {
free(deck[i]);
}
return 0;
}
If your compiler does not support the strdup function (most do, and it is part of the ISO C Standard from C23), writing your own is very simple:
char* strdup(const char *src)
{
char* result = malloc(strlen(src) + 1); // Add one to make room for the nul terminator
if (result) strcpy(result, src);
return result;
}
1 Well, formally, a new card array is born on each iteration of the inner for loop, but it would be a very inefficient compiler that chose to do that, rather than simply re-using the same memory – which is clearly what is happening in your case.
Related
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;
}
I've been trying to bubble sort an array of strings but I always get the "Segmentation fault" Error.
Any help is appreciated.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char *arreglo[20]={"Ruben","Modesta","Jan","Ana","Amparo","Josu","Azahara","Concepcio","Carmelo","Miguel","Francesc","Jairo","Jose","Luis","Teo","Jone","Jacobo","Ainoa","Natalia","Igor"};
int i;
int j;
char *temp;
for (int j=0; j<20; j++)
{
for (int i=j+1; i<20; i++)
{
if (strcmp(arreglo[j], arreglo[i]) > 0)
{
strcpy(temp, arreglo[j]);
strcpy(arreglo[j], arreglo[i]);
strcpy(arreglo[i], temp);
}
}
}
}
String literals are typically stored in read-only memory area, not intended to be modified.
In this question, there is no need to move the string literal. Instead, we can just modify the string pointers. It's faster and safer.
Please check this code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
const char *arreglo[20]={"Ruben","Modesta","Jan","Ana","Amparo","Josu","Azahara","Concepcio","Carmelo","Miguel","Francesc","Jairo","Jose","Luis","Teo","Jone","Jacobo","Ainoa","Natalia","Igor"};
int i;
int j;
const char *sorted[20];
for(i=0;i<20;i++){
sorted[i] = arreglo[i];
}
for (j=0; j<20; j++){
for (i=j+1; i<20; i++) {
if (strcmp(sorted[j], sorted[i]) > 0) {
const char *temp = sorted[j];
sorted[j] = sorted[i];
sorted[i] = temp;
}
}
}
for(i=0;i<20;i++){
printf("%d:%s\n",i,sorted[i]);
}
}
Pointers points to string literals are set to "const char *"
Hopefully this may help you solve your problem.
When you declare an array-of-pointers-to char and initialize each pointer to a string, each of those strings is a String Literal and cannot be modified (there are very few exceptions). In order to swap strings, each of the strings must reside in memory that can be modified -- like an array or within an allocated block of memory.
The simple solution in your case is to make arreglo a 2D array instead of an array-of-pointers, e.g. char arreglo[20][20]. You other option is to leave arreglo as an array-of-poiners, but then allocated storage for each pointer and then copy a name to each of the allocated blocks before sorting (a bit more involved).
You have the same issue with temp. You declare temp as a pointer, but it is not initialized to point to any valid storage before you attempt to use it in your swap routine. It too can simply be char temp[20]; for you data (though you only need declare it within the if statement)
Making those changes and tweaking the sort limits a bit, you could do:
#include <stdio.h>
#include <string.h>
int main (void) {
char arreglo[20][20] = {"Ruben", "Modesta", "Jan", "Ana", "Amparo", "Josu",
"Azahara", "Concepcio", "Carmelo", "Miguel", "Francesc",
"Jairo", "Jose", "Luis", "Teo", "Jone", "Jacobo",
"Ainoa", "Natalia", "Igor"};
for (int j = 0; j < 20 - 1; j++) {
for (int i = j + 1; i < 20; i++) {
if (strcmp(arreglo[j], arreglo[i]) > 0) {
char temp[20];
strcpy (temp, arreglo[j]);
strcpy (arreglo[j], arreglo[i]);
strcpy (arreglo[i], temp);
}
}
}
for (int i = 0; i < 20; i++)
puts (arreglo[i]);
}
(note: you do not need stdlib.h)
**Example Use/Output **
$ ./bin/bubblesort_2D_names
Ainoa
Amparo
Ana
Azahara
Carmelo
Concepcio
Francesc
Igor
Jacobo
Jairo
Jan
Jone
Jose
Josu
Luis
Miguel
Modesta
Natalia
Ruben
Teo
Look things over and let me know if you have questions.
I don't understand what is wrong with the code below. It should malloc a 2D char array[5][30] (referred as LENGTH), pass it to a function and fill it with a string. It works just fine in the function; I can print it from there without any problem. But i cannot print even the first one from within the main() function (the application crashes if I try).
Could somebody please explain what I am doing wrong?
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define LENGTH 5
void fillArray(char array[][LENGTH]) {
for (int i = 0; i < 5; i++) {
strcpy(array[i],"Hi World");
}
for (int i = 0; i < 5; i++) {
printf("%s\n",array[i]);
}
}
int main() {
char** array = (char**)malloc(5*sizeof(char*));
for (int i = 0; i < 5; i++) {
array[i] = (char*)malloc(LENGTH);
}
fillArray(array);
printf("%s",array[0]);
getchar();
return 0;
}
From main() function you are passing double pointer array and catching with 2D array array[][LENGTH] which is not correct, just saying double pointer is not similar to 2D array & vice versa.
for your task in fillArray() use array of char pointer as a argument, how many ? LENGTH.
void fillArray(char *array[LENGTH]) { /* this is okay */
for (int i = 0; i < 5; i++) {
strcpy(array[i],"Hi World");/*now in `array[i]` you can store any no of char. */
}
for (int i = 0; i < 5; i++) {
printf("%s\n",array[i]);
}
}
Also casting malloc() is not required And once job is done at last don't forget to free the dynamically allocated memory by calling free() for each.
The basic problem is that your function expects a 2 dimensional array but that is not what you pass to the function.
In main you allocate a 1 dimensional array of pointers. Then for each of these pointers, you allocate a 1 dimensional array of chars. That is not a 2D array.
So your function doesn't get what it expects and therefore your program fails.
So instead of:
char** array = (char**)malloc(5*sizeof(char*));
for (int i = 0; i < 5; i++) {
array[i] = (char*)malloc(LENGTH);
}
try this:
char (*array)[LENGTH] = malloc(5*LENGTH*sizeof(char*));
to get a correctly malloc'ed 2D array.
BTW:
I think you have a bug here
#define LENGTH 5
^
I guess you want 30 instead of 5
I made a c program that attempts to add the values of one string array to another using a separate method:
#include <stdio.h>
#include <stdlib.h>
void charConv(char *example[])
{
example= (char* )malloc(sizeof(char[4])*6);
char *y[] = {"cat", "dog", "ate", "RIP", "CSS", "sun"};
printf("flag\n");
int i;
i=0;
for(i=0; i<6; i++){
strcpy(example[i], y[i]);
}
}
int main() {
char *x[6];
charConv( *x[6]);
printf("%s\n", x[0]);
}
However it keeps returning a segmentation fault. I'm only beginning to learn how to use malloc and c in general and its been puzzeling me to find a solution.
To pin-point your problem: you send *x[6] (here - charConv( *x[6]);) which is the first char of the 7'th (!!!) string (Remember, C is Zero-Base-Indexed) inside an array of 6 string you didn't malloc -> using memory you don't own -> UB.
Another thing I should note is char[] vs char * []. Using the former, you can strcpy into it strings. It would look like this:
'c' | 'a' | 't' | '\0' | 'd' | 'o' | 'g' | ... [each cell here is a `char`]
The latter ( what you used ) is not a contiguous block of chars but a array of char *, hence what you should have done is to allocate memory for each pointer inside your array and copy into it. That would look like:
0x100 | 0x200 | 0x300... [each cell is address you should malloc and then you would copy string into]
But, you also have several problems in your code. Below is a fixed version with explanations:
#include <stdio.h>
#include <stdlib.h>
void charConv(char *example[])
{
// example= (char* )malloc(sizeof(char[4])*6); // remove this! you don't want to reallocate example! When entering this function, example points to address A but after this, it will point to address B (could be NULL), thus, accessing it from main (the function caller) would be UB ( bad )
for (int i = 0; i < 6; i++)
{
example[i] = malloc(4); // instead, malloc each string inside the array of string. This can be done from main, or from here, whatever you prefer
}
char *y[] = {"cat", "dog", "ate", "RIP", "CSS", "sun"};
printf("flag\n");
/* remove this - move it inside the for loop
int i;
i=0;
*/
for(int i=0; i<6; i++){
printf("%s\t", y[i]); // simple debug check - remove it
strcpy(example[i], y[i]);
printf("%s\n", example[i]); // simple debug check - remove it
}
}
int main() {
char *x[6];
charConv( x); // pass x, not *x[6] !!
for (int i = 0; i < 6; i++)
{
printf("%s\n", x[i]); // simple debug check - remove it
}
}
As #MichaelWalz mentioned, using hard-coded values is not a good practice. I left them here since it's a small snippet and I think they are obvious. Still, try to avoid them
You need to start by understanding the pointers and some other topics as well like how to pass an array of strings to a function in C etc.
In your program, you are passing *x[6] in charConv() which is a character.
Made corrections in your program -
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void charConv(char *example[], int num)
{
int i;
for (i = 0; i < num; i++){
example[i] = (char* )malloc(sizeof(char)*4);
}
const char *y[] = {"cat", "dog", "ate", "RIP", "CSS", "sun"};
for(i = 0; i < num; i++){
strcpy(example[i], y[i]);
}
}
int main() {
char *x[6];
int i = 0;
charConv(x, 6);
/* Print the values of string array x*/
for(i = 0; i < 6; i++){
printf("%s\n", x[i]);
}
return 0;
}
My aim is to write an app which generates an char - array (each should be random-filled with strings of the length 4) and sorts this array. The time this process takes should be measured. I coded the following:
#include <string.h>
#include <jni.h>
#include <time.h>
#include <math.h>
clock_t start, finish;
static int ARRAY_LENGTH = 200;
static int WORD_LENGTH = 4;
char values[200];
void sortStringArray(void){
int i, j;
for(i = 0; i < ARRAY_LENGTH; i++){
for(j = 0; j < ARRAY_LENGTH-1; j++){
if(strcmp(values[j], values[j+1]) > 0) {
char holder = values[j+1];
values[j+1] = values[j];
values[j] = holder;
}
}
}
}
char generateRandomChar(char aC[]){
int length = strlen(aC);
char randStr[WORD_LENGTH];
int m;
for(m = 0; m <WORD_LENGTH; m++){
int randNr = rand()%length;
randStr[m] = aC[randNr];
}
return randStr;
}
void fillStringArray(void)
{
char allowedChars[] = "abcdefghijklmnopqrstuvwxyz";
int k;
for(k = 0; k < ARRAY_LENGTH; k++){
char randStr = generateRandomChar(allowedChars);
values[k] = randStr;
}
}
double
Java_com_example_hellojni_HelloJni_processStringSort( JNIEnv* env, jobject thiz)
{
start = clock();
fillStringArray();
sortStringArray();
finish = clock();
return ((double)(finish - start));
}
Since I am pretty new to coding C, i am not that fimilar with the concept of pointers, and therefore i recieve some mistakes.
alt text http://img38.imageshack.us/img38/2894/androidndkdebugc.jpg
It would be helpful if sb could explain me where it would be useful to use a pointer in this code. Some help with the errors would be very appreciated.
Thanks! Ripei
Without re-writing your code from scratch, it is difficult to to know where to start. I'm afraid it is all wrong. In order to get a good understanding of pointer and character string use in C, you must read a good, authoritative book on the language, Luckily, C has one of the best such books in the world - The C Programming Language. If you haven't already got a copy, get one, and if you have, re-read the chapters on pointers and strings.
Well for one thing you seem to think that char means string.... sometimes? char means a character, a number between 0 and 255. As the warnings on line 15 say, values[j] and values[j+1] are not strings (char *), they are characters (char). You probably want to make values an array of strings, ie an array of arrays of characters.
The 2nd set of warnings you're getting are related to line 31, where you're returning an array of characters (a pointer) from a function that states that it returns a character. The compiler silently casts the pointer to a character (since a pointer is a number) and returns that. You'll end up with a random number, which is probably not what you want.
To fix this you'll have to make the function return a char *, but there's a catch. randStr is gone as soon as you get out of the function, thus making it impossible to return. You could use strdup to duplicate the string and, after you're done using it in your main function, you call free to get rid of it.
While we're on this function, the parameter to it should be a char *, not a char[]. They have different meanings.
The last message (the only error reported as such apparently) is because you didn't define rand(). Adding a #include <stdlib.h> at the beginning of the program should fix it.
Thank you very much Blindy for your hints. I tried to implement your hints. Now the program doesn't throw errors but the problem is that i can't check if the operation is done correctly with the programm-environment i've to work with. Do you think the code is correct as it is shown below? Also the time it takes is quite less: 11ms. Do I calc this right?
Neil Butterworth,... well your're probably right, but I had to start somehow... and I tried my best to do so.
Vinko Vrsalovic,... well you're not right ;) I did it step by step but I thought that its better to show you the whole program and all errors at one time.
#include <string.h>
#include <jni.h>
#include <time.h>
#include <stdlib.h>
long start, finish;
static int ARRAY_LENGTH = 500;
static int WORD_LENGTH = 4;
static int LOOPS = 10;
char *values[1000];
static long getTime(void){
struct timeval now;
gettimeofday(&now, NULL);
return (long)(now.tv_sec*1000 + now.tv_usec/1000);
}
void sortStringArray(void){
int i, j;
for(i = 0; i < ARRAY_LENGTH; i++){
for(j = 0; j < ARRAY_LENGTH-1; j++){
if(strcmp(values[j], values[j+1]) > 0) {
char *holder = values[j+1];
values[j+1] = values[j];
values[j] = holder;
}
}
}
}
char* generateRandomChar(char *aC){
int length = strlen(aC);
char randStr[WORD_LENGTH];
int m;
for(m = 0; m <WORD_LENGTH; m++){
int randNr = rand()%length;
randStr[m] = aC[randNr];
}
return strdup(randStr);
}
void fillStringArray(void)
{
char *allowedChars = "abcdefghijklmnopqrstuvwxyz";
int k;
for(k = 0; k < ARRAY_LENGTH; k++){
char *randStr = generateRandomChar(allowedChars);
values[k] = randStr;
}
}
jlong
Java_com_example_hellojni_HelloJni_processStringSort( JNIEnv* env, jobject thiz)
{
start = getTime();
int i;
for(i = 0; i < LOOPS; i++){
fillStringArray();
sortStringArray();
}
finish = getTime();
return (finish - start);
}