Error in dynamic creation of string array - c

I am trying to read strings from file and insert them into matrix. Every line is one word.
FILE *fp = fopen("zadanie4.txt","r");
if( fp == NULL)
{
perror("Error while opening the file.\n");
exit(EXIT_FAILURE);
}
int symbol, num_of_lines = 0, len_of_string = 0, max_len = 0;
do {
symbol = fgetc(fp);
len_of_string++;
if (symbol == '\n' || feof(fp)) {
num_of_lines++;
if(len_of_string > max_len){
max_len = len_of_string;
}
len_of_string = 0;
}
} while (symbol != EOF);
fclose(fp);
printf("Number of words: %d\n", num_of_lines);
printf("Longest word: %d\n", max_len);
fp = fopen("zadanie4.txt","r");
char (*arr)[num_of_lines] = calloc(num_of_lines, sizeof(char*) * max_len);
int index = 0;
while(fscanf(fp, "%s", arr[index++]) == 1) {
printf("%s\n", arr[index - 1]); //first check to see what is written into array
}
close(fp);
printf("--------------------------\n");
int i;
for(i = 0; i < num_of_lines; i++){
printf("%s\n", arr[i]); //second check
}
I find out size of longest string and allocate memory for number of strings * longest string.
Here is how output looks like if longest word is 5 (+1 for empty '\0'):
Number of words: 6
Longest word: 6
AAAAA
BBBBB
CCCCC
DDDDD
EEEEE
FFFFF
--------------------------
AAAAA
BBBBB
CCCCC
DDDDD
EEEEE
FFFFF
If I add another char to every line:
Number of words: 6
Longest word: 7
AAAAAa
BBBBBb
CCCCCc
DDDDDd
EEEEEe
FFFFFf
--------------------------
AAAAAaBBBBBbCCCCCcDDDDDdEEEEEeFFFFFf
BBBBBbCCCCCcDDDDDdEEEEEeFFFFFf
CCCCCcDDDDDdEEEEEeFFFFFf
DDDDDdEEEEEeFFFFFf
EEEEEeFFFFFf
FFFFFf
Note: Every string is same size in this example, but I want it to work for various sizes.
Can anybody aid me how to properly allocate memory for this array?

Your declaration and allocation of arr are wrong and don't match.
You declare arr as a pointer to an array of num_of_lines characters. Then you allocate max_len number of pointers to character.
You can use variable-length arrays for both the outer and inner arrays:
char arr[num_of_lines][max_len + 1]; // +1 for string terminator
No need for dynamic allocation at all.

I believe you don't really understand how calloc works. You can't assign memory to the members of an array using
char (*arr)[num_of_lines] = calloc(num_of_lines, sizeof(char*) * max_len);
The first parameter of calloc will be the amount of blocks to allocate for an array and the second parameter will be the size of each block in bytes.
Instead you want to define the array like this
char *arr[num_of_lines];
Now you need to loop through each member and allocate memory for it
for(int i = 0; i < num_of_lines; i++) {
arr[i] = malloc(sizeof(char) * max_len);
}
Alternatively you can just define the array as a static array because you know the size of max_len.

You're seeing that behavior because you are over-writing the string terminator (null character == 0) at the end of each string when you add the extra character.
Instead of using one contiguous block of memory, you'll want to keep an array of string pointers, and allocate each string separately:
char *strings[];
strings = calloc(sizeof(char*),NUMBER_OF_STRINGS);
for i = 0; i < NUMBER_OF_STRINGS; i++ {
strings[i] = calloc(sizeof(char), MAX_STRING_LENGTH+1);
}
Then, when you want to add a character to a string, use strcat if the new total is less than the max string length:
strcat(strings[i],"-suffix");
Or, if it is longer, you'll need to reallocate the storage:
strings[i] = realloc(strings[i], MAX_STRING_LEN+EXTRA_BYTES+1);
strcat(strings[i],"-suffix");

Related

HEAP CORRUPTION DETECTED: after normal block(#87)

I'm trying to do a program that get number of names from the user, then it get the names from the user and save them in array in strings. After it, it sort the names in the array by abc and then print the names ordered. The program work good, but the problem is when I try to free the dynamic memory I defined.
Here is the code:
#include <stdio.h>
#include <string.h>
#define STR_LEN 51
void myFgets(char str[], int n);
void sortString(char** arr, int numberOfStrings);
int main(void)
{
int i = 0, numberOfFriends = 0, sizeOfMemory = 0;
char name[STR_LEN] = { 0 };
char** arrOfNames = (char*)malloc(sizeof(int) * sizeOfMemory);
printf("Enter number of friends: ");
scanf("%d", &numberOfFriends);
getchar();
for (i = 0; i < numberOfFriends; i++) // In this loop we save the names into the array.
{
printf("Enter name of friend %d: ", i + 1);
myFgets(name, STR_LEN); // Get the name from the user.
sizeOfMemory += 1;
arrOfNames = (char*)realloc(arrOfNames, sizeof(int) * sizeOfMemory); // Change the size of the memory to more place to pointer from the last time.
arrOfNames[i] = (char*)malloc(sizeof(char) * strlen(name) + 1); // Set dynamic size to the name.
*(arrOfNames[i]) = '\0'; // We remove the string in the currnet name.
strncat(arrOfNames[i], name, strlen(name) + 1); // Then, we save the name of the user into the string.
}
sortString(arrOfNames, numberOfFriends); // We use this function to sort the array.
for (i = 0; i < numberOfFriends; i++)
{
printf("Friend %d: %s\n", i + 1, arrOfNames[i]);
}
for (i = 0; i < numberOfFriends; i++)
{
free(arrOfNames[i]);
}
free(arrOfNames);
getchar();
return 0;
}
/*
Function will perform the fgets command and also remove the newline
that might be at the end of the string - a known issue with fgets.
input: the buffer to read into, the number of chars to read
*/
void myFgets(char str[], int n)
{
fgets(str, n, stdin);
str[strcspn(str, "\n")] = 0;
}
/*In this function we get array of strings and sort the array by abc.
Input: The array and the long.
Output: None*/
void sortString(char** arr, int numberOfStrings)
{
int i = 0, x = 0;
char tmp[STR_LEN] = { 0 };
for (i = 0; i < numberOfStrings; i++) // In this loop we run on all the indexes of the array. From the first string to the last.
{
for (x = i + 1; x < numberOfStrings; x++) // In this loop we run on the next indexes and check if is there smaller string than the currnet.
{
if (strcmp(arr[i], arr[x]) > 0) // If the original string is bigger than the currnet string.
{
strncat(tmp, arr[i], strlen(arr[i])); // Save the original string to temp string.
// Switch between the orginal to the smaller string.
arr[i][0] = '\0';
strncat(arr[i], arr[x], strlen(arr[x]));
arr[x][0] = '\0';
strncat(arr[x], tmp, strlen(tmp));
tmp[0] = '\0';
}
}
}
}
After the print of the names, when I want to free the names and the array, in the first try to free, I get an error of: "HEAP CORRUPTION DETECTED: after normal block(#87)". By the way, I get this error only when I enter 4 or more players. If I enter 3 or less players, the program work properly.
Why does that happen and what I should do to fix it?
First of all remove the unnecessary (and partly wrong) casts of the return value of malloc and realloc. In other words: replace (char*)malloc(... with malloc(..., and the same for realloc.
Then there is a big problem here: realloc(arrOfNames, sizeof(int) * sizeOfMemory) : you want to allocate an array of pointers not an array of int and the size of a pointer may or may not be the same as the size of an int. You need sizeof(char**) or rather the less error prone sizeof(*arrOfNames) here.
Furthermore this in too convoluted (but not actually wrong):
*(arrOfNames[i]) = '\0';
strncat(arrOfNames[i], name, strlen(name) + 1);
instead you can simply use this:
strcpy(arrOfNames[i], name);
Same thing in the sort function.
Keep your code simple.
But actually there are more problems in your sort function. You naively swap the contents of the strings (which by the way is inefficient), but the real problem is that if you copy a longer string, say "Walter" into a shorter one, say "Joe", you'll write beyond the end of the allocated memory for "Joe".
Instead of swapping the content of the strings just swap the pointers.
I suggest you take a pencil and a piece of paper and draw the pointers and the memory they point to.

C Flatten 2-D Array of Char* to 1-D

Say I have the following code:
char* array[1000]; // An array containing 1000 char*
// So, array[2] could be 'cat', array[400] could be 'space', etc.
Now, how could I flatten this array into 1-D? Is it possible to do this such that new_1D_array[2] would still be 'cat', new_1D_array[400] would still be 'space', etc.?
You have a one-dimensional array of type pointer-to-char, with 1000 such elements. It's already 1D as far as arrays go, though it could be interpreted as a "jagged 2D array". If you want to convert this into one massive character array, you could do something like so, which requires calculating the size of the buffer you'll need to store it, and then flattening the array.
Note that if you opt to use malloc instead of calloc, you'll have to manually set the last character to '\0' or 0 so that the final result is a NULL-delimited C-style string, and not just a character array, as the latter of the two won't work with C string operations, and the former will.
Code Listing
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void) {
const char* array[] = { "one", "two", "three", "four" };
size_t length = sizeof(array) / sizeof(char*);
int i;
int sum = 0;
int count = 0;
for (i=0; i<length; i++) {
printf("Element #%d - %s\n", i, array[i]);
sum += strlen(array[i]) + 1;
}
sum++; // Make room for terminating null character
char* buf;
if ((buf = calloc(sum, sizeof(char))) != NULL) {
for (i=0; i<length; i++) {
memcpy(buf+count, array[i], strlen(array[i]));
count += strlen(array[i]) + 1;
buf[count-1] = '#';
}
printf("Output Buffer: %s\n", buf);
printf("Buffer size:%d - String Length:%d\n", sum, strlen(buf));
free(buf);
buf = NULL;
}
return 0;
}
Sample Output
Element #0 - one
Element #1 - two
Element #2 - three
Element #3 - four
Output Buffer: one#two#three#four#
Buffer size:20 - String Length:19
The above answer beat me to the puch and looks great, but I'll finish my thoughts here.
In this version every 11th byte of flatArray will contain the start of the strings from the original array, making it very easy to find the original strings from the ones before - i.e. string index 2 would start at 2*11 == index 22 and string 400 would start at 400 * 11 == index 4400. In this way, you would not need to iterate through the flat array counting terminators to find your old strings. The cost of this being that the flat array takes a bit more memory than the original.
char* array[1000];
// Malloc new buffer
char *flatArray = malloc(length * 1100);
for(i=0; i<1000; i++)
{
strncpy(&flatArray[i * 11], array[i], 10);
flatArray[i * 11 + 10] = '\0';
}

Print dynamically allocated array of integers on single line

I'm trying to do the following:
Open a file with up to 1000 ints in a text file, 1 int per line.
Read the file line by line
Store the ints in a dynamically allocated array of 1000
Print the contents of the array on a single line
Yes this is homework, I'm stuck on this.
Output should be:
$ 1 2 3 4 5 6 7 8 9 ....
What I have so far prints the integers on a new line after each iteration.
int x = 0;
char buf[1000];
int *array = (int *) malloc(1000 * sizeof(int));
FILE *fp = fopen("test.txt", "r");
while(fgets(buf, 1000, fp) != NULL) {
array[x] = buf;
printf("%s ", array[x]);
x++;
}
fclose(fp);
return 0;
}
Your code puts the buffer address in the integer array and then uses that as a string pointer for printf(), but you'll find it won't work if you try printing the array in a loop separate from the reading loop, because every element of the array holds this same buffer address. You should have been given a compiler warning about this.
This answer uses two loops, as you want to print the numbers after reading them.
while(fgets(buf, 1000, fp) != NULL && x < 1000)
array[x++] = atoi(buf);
for (y=0; y<x; y++)
printf("%d ", array[y]);
printf("\n");

C - Malloc char array access violation error

I'm not sure why malloc is allocating so much space. Here's a snippet of the problem code:
char * hamming_string = NULL;
void enter_params(){
printf("Enter the max length: ");
scanf_s("%d", &max_length);
hamming_string = (char *) malloc(max_length * sizeof(char));
// to test what's going on with the hamming string
for(int i = 0; i < strlen(hamming_string); i++){
hamming_string[i] = 'a';
}
printf("hamming string = %s", hamming_string);
}
I set max_length to 2 and I'm seeing 12 a's. In another function, I was going to have the user input the hamming string using scanf_s("%s", &hamming_string); but I kept getting a access violation
hamming_string is not a string until one of its elements is a '\0'.
The str*() functions can only be used on strings.
Your program invokes Undefined Behaviour (by calling strlen() with something that is not a string).
malloc() allocates the amount of space that you ask for but it does not initialise it. When you call strlen() it scans the memory starting at what hamming_string points to and continues until it finds a null or it accesses memeory that it shouldn't and causes an exception.
In addition you need to allocate space for the null at the end of the string, if you want a string to hold 2 characters you need to allocate 3 characters to allow for the terminating null.
You are asking for the strlen of an uninitialized variable (this is undefined behaviour):
strlen(hamming_string);
(m)allocate one more in order to store the trailling \0:
hamming_string = malloc(max_length + 1);
change to
for(int i = 0; i < max_length; i++){
hamming_string[i] = 'a';
}
and don't forget to add the trailling \0 after the for loop:
hamming_string[i] = '\0'; /* or use calloc and skip this line */
void check_code(){
int actual_length, parity_bit, error_bit = 0, c = 0, i, j, k;
printf("Enter the Hamming code: ");
scanf_s("%s", &hamming_string);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This scanf_s() call is incorrect.
According to the C11 documentation or MSDN documentation it needs to be
scanf_s("%s", hamming_string, size - 1);
Note that you don't know size inside the function.
Note that you don't pass the address of hamming_string; hamming_string by itself gets converted to the address of its first element.
Example1:
char *hamming_string = malloc((max_length + 1) * sizeof(char));
for (i = 0; i < max_length; i++)
{
hamming_string[i] = 'a';
}
hamming_string[i] = '\0';
printf("hamming string = [%s]\n", hamming_string);
Output:
sdlcb#Goofy-Gen:~/AMD$ ./a.out
hamming string = [aaaaaaaaaaaa]
Example2:
char s;
for (i = 0; i < max_length; i++)
{
scanf(" %c", &s);
hamming_string[i] = s;
}
hamming_string[i] = '\0';
printf("hamming string = [%s]\n", hamming_string);
Output:
sdlcb#Goofy-Gen:~/AMD$ ./a.out
a
b
c
d
e
f
g
h
i
j
k
l
hamming string = [abcdefghijkl]

Having issue with copy string values im between arrays

I have been working on some code that tokenizes a string from a line and then creates a temp array to copy the string into it (called copy[]) and it is filled with 0's initially (The end game is to split this copy array into temp arrays of length 4 and store them in a struct with a field char* Value). For some reason my temp arrays of size 4 end up having a size of 6.
char* string = strtok(NULL, "\"");
printf("%s", string);
int len = (int)strlen(string);
while(len%4 != 0) {
len++;
}
char copy[len];
for(int i = 0; i < len; i++){
copy[i] = '0';
}
printf("%s\n", copy);
int copyCount = 0;
int tmpCount = 0;
char temp[4];
while (copyCount < len) {
if(tmpCount == 4) {
tmpCount = 0;
}
while(tmpCount < 4) {
temp[tmpCount] = copy[copyCount];
tmpCount++;
copyCount++;
}
printf("%s %d\n", temp, (int)strlen(temp));
}
This yields:
This is the end
0000000000000000
This is the end0
This� 6
is � 6
the � 6
end0� 6
And should yield:
This is the end
0000000000000000
This is the end0
This 4
is 4
the 4
end0 4
I've been messing around with this for awhile and can't seem to figure out why its making temp have a length of 6 when I set it to 4. Also I'm not sure where the random values are coming from. Thanks!
The reason is that your string temp is not null-terminated. C-style strings should be terminated with a \0 character. For some (lucky) reason there is a \0 three bytes in memory after wherever the end of temp lives, so when strlen tries to compute its length, it gets 6. This is also why printf is printing garbage: it will print temp until it finds the null terminator, and there are garbage characters in memory before printf reaches the null terminator.

Resources