Sorting strings with C - c

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

Related

Initialize the arrays inside an array of char* using a loop

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.

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

exercise 9.7 Kochan. strange output

I am working to learn C using Kochan's Programming in C 4th edition. problem 9.7 the goal is to insert a string of characters into another array. I am supposed to write a function to accomplish this. I have two problems.
When I have the algorithm print the result as it goes through the if statements, it produces the desired output, however when I change it to an %s, I only get a partial output. My hunch is that a null character is being placed where i do not want it, but I simply cannot see it.
To see what was happening, I added a printf that would track the letter and the array space it was occupying. I was surprised to see that the first letter was not 0, but was blank, and the next letter was assigned the 0. Any insight into this would be appreciated.
The funtion of interest is "insertString".
#include <stdio.h>
#include <stdbool.h>
char x[] = {"the wrong son was shot that day"};
char text[] = {"per"};
int countString (char x[])
{
int counter, z;
for (counter = 0; x[counter] != '\0'; ++counter)
z = counter+1;
return z;
}
void insertString (char text[],char x[],int n) //source, text to input, where
{
int count, clock, i = countString(text), q = countString(x);
int counter = 0;
char y[i + q];
for(count = 0; x[count] != '\0'; ++count){
if (count < n){
y[count] = x[count];
printf("%c %i", y[count], count); //The integer call is just to put a number next to the
//letter. This is where my second issue is shown.
}
else if (counter <= i){
y[count] = text[counter];
++counter;
printf("%c", y[count]);
}
else{
y[count]= x[count - counter];
printf("%c", y[count]);
}
}
printf("\n\n");
y[count-counter] = '\0';
printf("%s", y);
}
int main (void)
{
void insertString(char text[], char x[], int i);
int countString(char x[]);
int i;
insertString(text, x, 10);
return 0;
}
10 out of 10 times I post here it is because im doing something dumb, so I use SO as an absolute last resort if i am getting into the territory of just randomly trying stuff with no methodology. Thanks for your patience in advance.
Your condition is wrong in the for. It should be x[count - counter] != '\0'
In the second condition use just < to avoid overindexing. (else if (counter < i))
You put the terminating NULL char at wrong place. You should do this: y[count] = '\0'
printf inside a string routine like this is fine for debugging, but it's a poor way to write a general-purpose function because it makes it impossible to use its output for further programmatic manipulation. It can also make it difficult to reason about how the state of the function interacts in unpredictable ways with the state of the printed data.
I assume you haven't learned about dynamic memory allocation which is a prerequisite to returning strings from functions. You can inline the function logic into main or printf only at the end of the function in the meantime.
Adding to this point, a void function would need to reallocate space in the string to insert into and would be in-place. This seems likely less generally useful than allocating a new string to hold the result.
Using global variables like char x[] when there's no need is poor practice. It's better to put those strings scoped to main. Since your function can access these variables in addition to its parameters, confusion can ensue when scope and encapsulation is breached.
Use consistent formatting and avoid variable names like q that mean virtually nothing. Instead of adding comments to explain poor var names:
void insertString (char text[],char x[],int n) //source, text to input, where
You can simply name the variables exactly what they represent:
void insertString(char *dest, char *source, int add_index)
Also, now that you've mastered countString, you can abstract this by calling the builtin strlen.
Be sure to allocate enough space in buffers: char y[i + q]; should be y[i+q+1] to allow room for the null terminator '\0'.
As for the logic, I think it's easier to break into three loops without conditions instead of one loop with conditions. This makes it easier to break the problem down into the three constituent steps:
Add everything up until add_index from the dest string to the result.
Add everything in the source string to the result.
Add everything after add_index from the dest string to the result.
Using this approach, all that's left is figuring out how to map the indexes appropriately. Here it is in code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *insert_string(char *dest, char *source, int add_index) {
int source_len = strlen(source);
int dest_len = strlen(dest);
int result_size = source_len + dest_len + 1;
char *result = malloc(result_size);
for (int i = 0; i < add_index; i++) {
result[i] = dest[i];
}
for (int i = 0; i < source_len; i++) {
result[i+add_index] = source[i];
}
for (int i = add_index; i < dest_len; i++) {
result[i+add_index] = dest[i];
}
result[result_size-1] = '\0';
return result;
}
int main(void) {
char *result = insert_string("hello world", "cruel ", 6);
printf("%s\n", result);
free(result);
return 0;
}
Although this is likely for instructional purposes, these operations can be abstracted further using builtin string functions like strncpy and sprintf.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *insert_string(char *dest, char *source, int add_index) {
int result_size = strlen(dest) + strlen(source) + 1;
char *result = malloc(result_size);
char pre[add_index+1];
pre[add_index] = '\0';
strncpy(pre, dest, add_index);
sprintf(result, "%s%s%s", pre, source, dest + add_index);
return result;
}
int main(void) {
char *result = insert_string("hello world", "cruel ", 6);
printf("%s\n", result);
free(result);
return 0;
}
Doing this in-place is more straightforward. Since the result already has the prefix, you can copy the destination postfix to create a source-sized gap in the middle and then overwrite the gap using the source string. It's up to the caller to make sure that the destination buffer is large enough to hold the insertion.
#include <stdio.h>
#include <string.h>
void insert_string(char *dest, char *source, int add_index) {
int source_len = strlen(source);
int dest_len = strlen(dest);
for (int i = add_index; i < dest_len; i++) {
dest[i+add_index] = dest[i];
}
for (int i = 0; i < source_len; i++) {
dest[i+add_index] = source[i];
}
}
int main(void) {
// allocate extra space in the string to hold the insertion
char greeting[32] = "hello world";
insert_string(greeting, "cruel ", 6);
printf("%s\n", greeting);
return 0;
}
A note of caution: none of these functions handle errors at all, so they're unsafe. Correct functions should check that the add_index falls within the bounds of the dest string. This is an exercise for the reader.
The original exercise is here:
Your function is not doing it. You need to insert the string into another string not to create a new one with both mixed. You can do it this way of course and then copy it into the original one - but it is the most uneficient way to archive it (memory & timewise).
Use the correct types.
size_t mystrlen(const char *str)
{
const char *end = str;
while(*end++);
return end - str - 1;
}
char *strinsert(char *dest, size_t pos, const char *istr)
{
char *temp = dest, *work;
size_t ilen = mystrlen(istr);
size_t nmove;
while(*temp) temp++;
nmove = temp - dest - pos + 1;
work = temp;
temp += ilen;
while(nmove--) *temp-- = *work--;
work = dest + pos;
while(*istr) *work++ = *istr++;
return dest;
}
int main()
{
char dest[128] = "0123456789012345678901234567890123456789";
printf("%s", strinsert(dest, 7, "ABCD"));
}
https://godbolt.org/z/KMnLU2

memcpy() and memmove() not working as expected

I ran into this problem while working with a struct array within a struct. I'm trying to set the entry array in the dict using memcpy. I'm getting a mixture of the expected values and some seemingly random integers as output.
Oddly enough, I tried this code with some online compilers, and it worked fine. I thought that it might have to do with overlapping memory regions, so I tried memmove() but the result was the same.
I think I might be using malloc improperly for the dict, but I'm not sure. It seems I can't use malloc for the internal array, or for the individual elements. I'd appreciate any help.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct{
int i;
} entry;
typedef struct{
entry e[10];
int i;
} dict;
dict* d;
void test(dict* di){
printf("%d\n", di->i);
int k;
for (k = 0; k < 10; k ++){
printf(("%d\n"),di->e[k].i);
}
}
int main(){
entry en[10];
d = malloc(sizeof(d));
int k;
for (k = 0; k < 10; k++){
en[k].i = k;
}
d->i = 50;
memcpy(d->e, en, 10*sizeof(entry));
test(d);
return 0;
}
d = malloc(sizeof(d));
d is a dict*; sizeof d is sizeof(dict*). Looks like you meant sizeof *d.

Error with ToString()

I'm new to c. Please help me
Why do I get this error using eclipse
Multiple markers at this line
- request for member 'ToString' in something not a structure or union
- Method 'ToString' could not be resolved
Here is my code
#include <stdio.h>
int main()
{
int s = 5;
int n = 4;
char g = s.ToString();
char l = n.ToString();
printf(g+l);
return 0;
}
s and n are just ints; they don't have a ToString() method. Also, as #remyabel pointed out, char is not the appropriate type for storing a string value, anyway; it stores only one character.
You don't need to convert your ints to strings at all to do what you're trying to accomplish, so you actually want something like this:
#include <stdio.h>
int main()
{
int s = 5;
int n = 4;
printf("%d%d", s, n); // you can't add l to g here!
return 0;
}
// output 54
DEMO
Oh, and please use more descriptive variable names!
EDIT: To save the string, as requested in the comments, you could do this:
char myString[10];
sprintf(myString, "%d%d", s, n); // myString is now "54"
I'd suggest picking up a C tutorial and starting from the beginning. The use of ToString isn't the only thing that's wrong. You could rewrite it this way and it should work (assuming you want to print "54"):
#include <stdio.h>
int main()
{
int s = 5;
int n = 4;
char g = '0' + s;
char l = '0' + n;
printf("%c%c", g, l);
return 0;
}
But this only works as long as s and n are less than 10, and besides is overly complicated since printf is made for formatting and printing values of different types. This would work just as well:
#include <stdio.h>
int main()
{
int s = 5;
int n = 4;
printf("%d%d", s, n);
return 0;
}
If you want to use the string for something else than printing, the answer depends on what you want to do.

Resources