How to create an array of strings of unknown size? - c

I'm trying to understand how can I create a C program that declares an "array of strings" whose size is unknown at the time of declaration. This is what I've got so far:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int n, i;
char** words;
printf("How many strings you want to input? \n");
scanf("%d", &n);
words = malloc(sizeof(char*) * n);
for (i = 0; i < n; i++) {
printf("Input your %d string: ", i + 1);
scanf("%s", words[i]);
}
for (i = 0; i < n; i++) {
printf("%s\n", words[i]);
}
return 0;
}
The program compiles, but I get a Segmentation fault error.

You only allocated memory for the pointer to the strings, but not for the strings themselves. Attempting to store strings at non-allocated memory invokes undefined behavior.
The pointer are just pointer. You cannot store strings in them. You need to reserve memory for where the pointer should point to.
#define STR_SIZE 100 // max length of a string (incl. null terminator)
printf("How many strings you want to input? \n");
if (scanf("%d", &n) != 1)
{
fputs("Error at input", stderr);
// further error routine.
}
// allocate memory for the pointer to the strings.
words = malloc(sizeof(*words) * n);
if (!words)
{
// error routine if the memory allocation failed.
perror("malloc");
exit(EXIT_FAILURE);
}
// allocate memory for the strings themselves.
for (int i = 0; i < n; i++)
{
words[i] = malloc(sizeof(**words) * STR_SIZE);
if (!words[i])
{
// error routine if the memory allocation failed.
perror("malloc");
exit(EXIT_FAILURE);
}
}
Side notes:
Always check the return of memory-management functions if an error happened at the allocation! Same goes for input operations such as scanf().
Note that using sizeof(*words) and sizeof(**words) instead of sizeof(char*) and sizeof(char) is more safe for the case your code changes.

You did not allocate the memory properly for individual char pointer words[i].
This was ok - you allocated memory for the pointer to pointer to char words:
words = malloc(sizeof(char*) * n);
But then you haven't allocated for individual pointers of that words yet. To do so, you need to provision how much buffer to for individual word. As an example, if each word is 100 char, then:
for (i = 0; i < n; i++)
{
printf("Input your %d string: ", i + 1);
words[i] = malloc(sizeof(char) * 100); //<--
scanf("%s", words[i]);
}
Also remember to free the buffer once you're done.
for (int i=0; i<n; ++i) {
free(words[i]);
}
free(words);
return 0;

Related

How can I create a 2D array of chars with POINTERS in C?

I'm trying to create a program which the user inputs the number of items (rows) and give each one of them a name (scanf) with the max of 30 characters.
I want to create this code with pointers of pointers, once that I'm learning this on C.
I'm having some difficulties with the code.
Draft of the 2D array.
Code snippet:
PS: #define MAX 31
char **items = NULL, *columns = NULL, name[MAX];
int rows, aux_rows;
printf("\nNumber of items:\n");
scanf("%d", &rows);
items = (char **) malloc(rows * sizeof(char*));
for (aux_rows = 0; aux_rows < rows; aux_rows++){
columns[aux_rows] = (char *) malloc(MAX * sizeof(char));
}
for (aux_rows = 0; aux_rows < rows; aux_rows++){
printf("\nName of item %d:\n", (aux_rows + 1));
scanf("%s", name);
*items[aux_rows] = name;
}
items was allocated and not columns. And use strcpy to copy the characters.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 31
int main()
{
char **items = NULL, *columns = NULL, name[MAX];
int rows, aux_rows;
printf("\nNumber of items:\n");
scanf("%d", &rows);
items = (char **)malloc(rows * sizeof(char *));
for (aux_rows = 0; aux_rows < rows; aux_rows++)
{
items[aux_rows] = malloc(MAX * sizeof(char));
}
for (aux_rows = 0; aux_rows < rows; aux_rows++)
{
printf("\nName of item %d:\n", (aux_rows + 1));
scanf("%s", name);
strcpy(items[aux_rows], name);
}
return 0;
}
$ gcc array2d.c
$ ./a.out
Number of items:
2
Name of item 1:
Hello
Name of item 2:
World!
$
*items[aux_rows] = name;
is wrong on two counts.
Both * and [] dereference their unary operand. If items is a char **, items[n] is a char *, and *items[n] is a char.
This attempts to assign an array to the first element of each buffer.
Secondly, arrays cannot be copied by assignment. Use strcpy to copy strings from one buffer to another.
That said, you could simply read your strings directly into the pre-allocated buffers, and do away with the temporary buffer.
In this line,
columns[aux_rows] = (char *) malloc(MAX * sizeof(char));
columns should be items.
Some things of note:
sizeof (char) is guaranteed to be 1. Its use is superfluous.
The return of malloc should not be cast in C.
malloc can fail. scanf can fail. You should get in the habit of not ignoring return values.
scanf("%s", ...) is as dangerous as gets. At a minimum, use field-width specifiers to limit input (should be the size of your buffer minus one).
char foo[128];
if (1 != scanf("%127s", foo))
/* handle error */;
Note that using the %s limits input to not contain any whitespace. scanf in general is a terrible tool, consider a line based approach using fgets.
With that said, the minimum changes to make this reasonably safe:
#include <stdio.h>
#include <stdlib.h>
#define MAX 31
void die(const char *msg)
{
fprintf(stderr, "%s\n", msg);
exit(EXIT_FAILURE);
}
int main(void)
{
size_t rows;
printf("Number of items: ");
if (1 != scanf("%zu", &rows))
die("Failed to read input.");
char **items = malloc(sizeof *items * rows);
if (!items)
die("Failed to allocate memory.");
for (size_t i = 0; i < rows; i++) {
if (!(items[i] = malloc(MAX)))
die("Failed to allocate row.");
printf("Name of item %zu: ", i + 1);
if (1 != scanf("%30s", items[i]))
die("Failed to read item input.");
}
for (size_t i = 0; i < rows; i++) {
puts(items[i]);
free(items[i]);
}
}

Assigning char array of pointers with scanf

I'm trying to use scanf to fill an array of char pointers to store the input as a string. The variable T is used to build an array of size T dynamically. Then T amount of strings are entered and displayed however when I fill in the array for example if T = 2 the first line could dog and the second line cat, it prints out "cdog" and "cat". So the first letter of the first string then the all of the 2nd string. I'm not sure where my mistake is in using char*. Any help would be appreciated.
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
int main()
{
int T;
int i;
scanf("%d",&T);
char *new_array = (char*)malloc(T * sizeof(char));
for (i = 0; i < T; ++i)
{
scanf("%s", new_array+i);
}
for (i = 0; i < T; i++)
{
printf("%s\n", new_array+i);
}
}
Always check the return value of scanf().
You are not allocating space for pointers, but for bytes, which is the main problem, you need
char **new_array = malloc(T * sizeof(char *));
/* ^ ^ */
/* allocate pointer to poitners sizeof(pointer) */
if (new_array == NULL)
handleThisErrorAndDoNotTryToWriteTo_new_array();
you will also need space for each string so
new_array[i] = malloc(1 + lengthOfTheString);
if (new_array[i] == NULL)
handleThisErrorAndDoNotTryToWriteTo_new_array_i();
right before scanf(), and instead of scanf("%s", new_array + i) do this
scanf("%s", new_array[i]);
If you enable compiler warnings, the compiler should warn you that you are passing incompatible types to printf().
It would also be good, to use a length modifier for scanf() to prevent buffer overflow, and don't forget to call free() when you no longer need the pointers.
In your code, new_array is of type char *, which is not what you want. You have to change your definition to
char *new_array[T] = malloc(T * sizeof(char*));
Then, you can use the scanf() as per your previous code.
Do this instead, together with the rest of the body:
int string_size;
//this begins just after reading T
scanf("%d", &string_size);
char **new_arrays = malloc(T * sizeof(char*));
for(i = 0; i < T; i++)
{
new_arrays[i] = malloc(string_size * sizeof(char));
}
The first malloc is to specify how many strings you want, and the second malloc is to specify how big a string can be.
Further tips:
When you're writing in C, do not cast void* produced by malloc and realloc.
You should de-allocate the memory used in the reverse way:
for (i = 0; i < T; ++i)
{
free(new_array[i]);
}
free(new_array);
Always check if the memory allocation process is (un)succesful:
char **new_arrays = malloc(T * sizeof(char*));
if(new_arrays == NULL)
exit(0) //e.g.
for(i = 0; i < T; i++)
{
new_arrays[i] = malloc(string_size * sizeof(char));
if(new_arrays[i] == NULL)
exit(0) //e.g.
}
Check if the user provides valid values through scanf.
Thanks everyone. The length of the strings in the char* array can not be greater than 1000 chars so thats hard coded. Here is the final working code...
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
int main()
{
int T;
int i;
scanf("%d",&T);
char **new_array = malloc(T * sizeof(char*));
for (i = 0; i < T ; i++)
{
new_array[i] = malloc(1000 * sizeof(char));
scanf("%s", new_array[i]);
}
for (i = 0; i < T; i++)
printf("%s\n", new_array[i]);
}

Array filling itself with only the last user entered inputed string

I m trying to make a piece of code that prompts the user for a number of string, n, dynamically allocates an array of n strings, and then prompts the user to enter n strings.
The problem I am having is the array is only showing itself being filled with the last string the user entered.
Example:
Program prompts user for n
User enters 3.
User enters "test1" for the first element in the array
User enters "test2" for the second element in the array
User enters "test3" for the third element in the array
I go to print the contents of the array, and it says each element is "test3"
Here is the code:
(flush_buffer() and strip_newline() are functions I wrote that are unimportant to the problem I am having)
printf("How many strings?\n");
scanf("%d", &max_strings);
flush_buffer();
string_array = (char**) malloc(max_strings * sizeof(char*));
for(i = 0; i < max_strings; i++)
{
scanf("%s", temp);
strip_newline(temp);
string_array[i] = temp;
printf("string_array[%d] is: %s\n", i, string_array[i]);
}
for(i = 0; i < max_strings; i++)
{
printf("i: %d\n", i);
printf("string_array[%d] is: %s\n", i, string_array[i]);
}
Any ideas on what I am missing here?
With the assignment
string_array[i] = temp;
you make all pointers in string_array point to the same place.
I suggest you use strdup to duplicate the string instead:
string_array[i] = strdup(temp);
Of course, this means you have to free all strings in the collection.
When you assign the strings:
string_array[i] = temp;
you just store a pointer of the temporary buffer in each string, which will be overwritten after the next string is read. In other words, all of your strings have the same value, namely temp, which has the contents of the last string read.
If you want to store your strings, you must allocate memory for string_array[i] and then copy the contents with strcpy. Alternatively, you can use a shortcut:
string_array[i] = strdup(temp);
Note that strdup allocates memory internally that you must free() at some point.
you can use this code:
int max_strings;
printf("How many strings?\n");
scanf("%d", &max_strings);
//allocate memory
char **string_array = (char**) malloc(max_strings * sizeof(char*));
for (int i = 0; i < max_strings; i++)
{
string_array[i] = (char*)malloc(sizeof(char) * 50);
}
for(int i = 0; i < max_strings; i++)
{
scanf("%s", string_array[i]);
printf("string_array[%d] is: %s\n", i, string_array[i]);
}
for(int i = 0; i < max_strings; i++)
{
printf("i: %d\n", i);
printf("string_array[%d] is: %s\n", i, string_array[i]);
}
//free memory
for (int i = 0; i < max_strings; i++)
{
free(string_array[i]);
}
free(string_array);

fgets "Access violation writing location 0xCCCCCCCC." error

Error: Unhandled exception at 0x60092A8D (msvcr110d.dll) in C_Son60.exe: 0xC0000005: Access violation writing location 0xCCCCCCCC.
When below code executed this error code is given.(Compiles successfully)
Where is my mistake?
#include <stdio.h>
int i;
int main(void){
char *names[3];
//get the names of the cities
puts("Enter names of cities");
for (i = 0; i < 3; i++)
{
fgets( names[i], 99, stdin);
}
//print entered names
for (i = 0; i < 3; i++)
{
printf("%s", *names[i]);
}
getch();
}
You need to allocate memory to which the char pointers point to before you read them in
for instance:
for (i = 0; i < 3; i++)
{
names[i] = malloc(200);
fgets( names[i], 99, stdin);
}
2 things:
you need to allocate the strings you created- you can do it with malloc(100 * sizeof(char))
when printing you do *names[i] which means - **(names + i).
all you need is names[i]
use the code:
#include <stdio.h>
int i;
int main(void){
char *names[3];
//get the names of the cities
puts("Enter names of cities");
for (i = 0; i < 3; i++)
{
names[i] = (char *)malloc(100 * sizeof(char));
fgets( names[i], 99, stdin);
}
//print entered names
for (i = 0; i < 3; i++)
{
printf("%s", names[i]);
}
getch();
}
You must allocate your memory before storing anything into it. When you need to allocate an array of elements, and you do not know the number of elements at compile time, you must use malloc() to allocate them.
Don't forget to free your dynamically allocated memory later in order to avoid memory leaks!

Getting words from text file to an array

My text file is formated like that:
my.txt
Red
Green
Blue
Yellow
I'm tring to get words like that:
typedef char * string;
main(){
int i;
string array[4];
FILE *my;
my = fopen("my.txt","r");
for(i = 0; i < 4; i++)
fscanf(data, "%s", &array[i]);
fclose(my);
}
When I try to print the array there is an error. What's wrong with my code and how can I fix it?
You'll need to allocate memory for your null-terminated strings.
At the moment you are only allocating memory for 4 char *, but these pointers are uninitialized and therefor will result in UB (undefined behavior) when you try to write data to the memory pointed to by them.
Working example snippet
The use of "%127s" in the below snippet is to prevent us writing outside the bounds of the allocated memory. With the format-string in question we will at the most read/write 127 bytes + the null-terminator.
Please remember that further error checks should be implemented if this is to be used in "real life".
Check to see that file_handle is indeed valid after attempt to open the file
Check to see that malloc did indeed allocate requested memory
Check to see that fscanf read the desired input
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int
main (int argc, char *argv[])
{
int i;
char * lines[4];
FILE *file_handle = fopen ("my.txt", "r");
for (i =0; i < 4; ++i) {
lines[i] = malloc (128); /* allocate a memory slot of 128 chars */
fscanf (file_handle, "%127s", lines[i]);
}
for (i =0; i < 4; ++i)
printf ("%d: %s\n", i, lines[i]);
for (i =0; i < 4; ++i)
free (lines[i]); /* remember to deallocated the memory allocated */
return 0;
}
output
0: Red
1: Green
2: Blue
3: Yellow
You try to read some data, but you don't have anywhere to put it. All you have is 4 pointers, pointing to god knows where and you are trying to write into it!
There are many ways to do this:
You know the bound to the size of the data:
#include <stdio.h>
#define MAX_CHARS 20
typedef char string[MAX_CHARS+1]; // leave one space for '\0'
main(){
int i;
string array[4];
FILE *my;
my = fopen("my.txt","r");
for(i = 0; i < 4; i++)
fscanf(data, "%s", array[i]); // no need for & with %s
fclose(my);
}
Assume a bound to the size of the data, and ignore the rest of the strings (if it was too big):
#include <stdio.h>
#define MAX_CHARS 20
#define MAX_CHARS_STR "20" // there are better ways to get this
typedef char string[MAX_CHARS+1];
main(){
int i;
string array[4];
FILE *my;
my = fopen("my.txt","r");
for(i = 0; i < 4; i++){
fscanf(data, "%"MAX_CHARS_STR"s", &array[i]); // read at most 20 chars for the string
ungetc('x', data); // append one character to make sure we don't hit space
fscanf(data, "%*s"); // ignore whatever is left of string
}
fclose(my);
}
Read the file twice, first time find out the sizes of each string (or the maximum size, for simplicity), then allocate memory for the strings (using malloc). Then read the file again and this time actually store the strings:
#include <stdio.h>
typedef char *string;
main(){
int i;
string array[4];
int cur_size = 0;
FILE *my;
my = fopen("my.txt","r");
for(i = 0; i < 4; i++){
fscanf(data, "%*s%n", &cur_size);
array[i] = malloc((cur_size+1)*sizeof(*array[i]));
}
fclose(my);
my = fopen("my.txt","r");
for(i = 0; i < 4; i++){
fscanf(data, "%s", array[i]);
}
fclose(my);
// and when done:
for(i = 0; i < 4; i++){
free(array[i]);
}
}
Read from the input chunk by chunk. For each string, if input string was not finished yet resize the memory allocated for the string (increase its size), read another chunk and check again. Method 3 is faster though and I recommend it, but just so you know, this is basically what happens in C++'s string.
Since all the other answers told you what you did wrong but not how to fix it. Here
typedef char * string;
#define LEN 100 //long enough for your line
main(){
int i;
string array[4];
for(i = 0; i < 4; i++) {
if((array[i] = (char *)(malloc(sizeof(char) * LEN))) == NULL) {
printf("malloc failed");
return 1;
}
}
FILE *my;
my = fopen("my.txt","r");
for(i = 0; i < 4; i++)
fscanf(data, "%s", &array[i]);
fclose(my);
}
And like they said you made space for the pointers but not for what the pointers point to.

Resources