First, let's straighten out a few issues with the posted code. size_t
is the correct type for array indices and sizes, so the .size
member of the s
structure should have type size_t
. This also means that the ChainOfCharacters()
function should take an argument of type size_t
, and this has ramifications for the format string specifiers in the calls to printf()
and scanf()
.
A user may not enter a number at the input prompt, in which case no assignment would be made; but since the posted code does not check the value returned from scanf()
to verify that a correct input was made, the code would continue with an indeterminate value in that .size
field, leading to undefined behavior. The code below checks for this, and exits with an error message if the user fails to input a number here, though this input validation could be further improved.
Note that it is better to use the EXIT_FAILURE
macro than to use -1
, as this is clearer and more portable.
The ChainOfCharacters()
function does not return a string, but only a pointer to an array of characters, so the characters will need to be printed one-by-one.
Note that there is no need to cast the result of malloc()
in C, and it is better to use identifers rather than explicit types for operands of the sizeof
operator: this is less error-prone and easier to maintain when types change.
The loop that assigns characters in the ChainOfCharacters()
function is needlessly complex and contains an error, possibly as a result of this complexity; the tabc
pointer points to one past the end of the allocated storage when it is returned. This can be resolved by rewriting the loop to use an index instead of pointer arithmetic. It is generally clearer and less error-prone to use array indexing instead of pointer arithmetic when possible. Avoid using magic numbers: rand() % ('z' - 'a') + 'a'
is much clearer in intent than rand()%(122-97)+97
(and don't be afraid to use a little whitespace). Further, note that the C Standard makes few restrictions on the character encodings that may be used by an implementation, and in particular this is not required to be ASCII. The letters of the Latin alphabet need not even be encoded in a contiguous sequence, as is the case with EBCDIC (which still exists in the real world). This is unlikely to be a problem here, but know that there are portable ways to handle this issue.
To assign the result from ChainOfCharacters()
, simply assign the pointer to the appropriate array[][]
field:
array[i][j]->var = ChainOfCharacters(array[i][j]->size);
To print the contents of the .var
fields, iterate over the array, and for each struct
, loop over the contents of the allocated storage for .var
, printing the characters:
/* print characters */
for (size_t i = 0; i < rows; i++)
{
for (size_t j = 0; j < columns; j++)
{
printf("array[%zu][%zu]->val: ", i, j);
for (size_t k = 0; k < array[i][j]->size; k++) {
putchar(array[i][j]->var[k]);
}
putchar('\n');
}
}
After all of this, you will need remember to free()
the allocated memory. Here is a complete program that implements these changes.
#include <stdio.h>
#include <stdlib.h>
struct s
{
size_t size;
char *var;
};
char* ChainOfCharacters(size_t liczba);
int main(void)
{
size_t rows = 3;
size_t columns = 3;
struct s *array[rows][columns];
for (size_t i = 0; i < rows; i++)
{
for (size_t j = 0; j < columns; j++)
{
array[i][j] = malloc(sizeof *array[i][j]);
printf("Give the size to structure nr. %zu: \n",
i * columns + j + 1);
if (scanf("%zu", &(array[i][j]->size)) != 1) {
fprintf(stderr, "Incorrect input\n");
exit(EXIT_FAILURE);
};
array[i][j]->var = ChainOfCharacters(array[i][j]->size);
}
}
/* print characters */
for (size_t i = 0; i < rows; i++)
{
for (size_t j = 0; j < columns; j++)
{
printf("array[%zu][%zu]->val: ", i, j);
for (size_t k = 0; k < array[i][j]->size; k++) {
putchar(array[i][j]->var[k]);
}
putchar('\n');
}
}
/* free allocated memory */
for (size_t i = 0; i < rows; i++)
{
for (size_t j = 0; j < columns; j++)
{
free(array[i][j]->var);
free(array[i][j]);
}
}
return 0;
}
char* ChainOfCharacters(size_t liczba)
{
char *tabc = NULL;
tabc = malloc(sizeof *tabc * liczba);
if (tabc == NULL) {
exit(EXIT_FAILURE);
} else {
for (size_t i = 0; i < liczba; i++) {
tabc[i] = rand() % ('z' - 'a') +'a';
}
return tabc;
}
}
Sample interaction:
Give the size to structure nr. 1:
1
Give the size to structure nr. 2:
2
Give the size to structure nr. 3:
3
Give the size to structure nr. 4:
9
Give the size to structure nr. 5:
8
Give the size to structure nr. 6:
7
Give the size to structure nr. 7:
4
Give the size to structure nr. 8:
5
Give the size to structure nr. 9:
6
array[0][0]->val: i
array[0][1]->val: lc
array[0][2]->val: psk
array[1][0]->val: lryvmcpjn
array[1][1]->val: bpbwllsr
array[1][2]->val: ehfmxrk
array[2][0]->val: ecwi
array[2][1]->val: trsgl
array[2][2]->val: rexvtj
On choosing correct types
As I said at the beginning of the answer, size_t
is the correct type for array indices, as it is an unsigned
type that is guaranteed to be able to hold any array index value. But, unsigned
is also fine, though unsigned int
and size_t
may not have the same ranges.
A significant problem in the OP code is that the .size
field is unsigned
, while the scanf()
statement that stores input in this field uses the d
conversion specifier, which is meant to be used with int
s. According to the Standard, mismatched conversion specifiers and arguments lead to undefined behavior, which includes appearing to work in some instances. But you can't rely on undefined behavior doing what you expect. In the posted code,%u
should have been used to store an unsigned
value in the .size
field. Further, the ChainOfCharacters()
function was declared to accept an argument of type int
, but was called with an unsigned
argument (from .size
). This may also lead to implementation-defined behavior, since an unsigned
value may not be representable in an int
.
Another place that this problem could arise is in the loop that prints the characters. For example, consider:
struct s
{
unsigned size;
char *var;
};
/* ... */
int rows = 3;
int columns = 3;
struct s *array[rows][columns];
/* ... */
/* print characters */
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < columns; j++)
{
printf("array[%d][%d]->val: ", i, j);
for (int k = 0; k < array[i][j]->size; k++) {
putchar(array[i][j]->var[k]);
}
putchar('\n');
}
}
Here, k
is a signed int
, while array[i][j]->size
is an unsigned int
type, so the value of k
will be converted to an unsigned
value before the comparison is made. This conversion is well-defined, but can lead to surprises if k
is negative.
Enabling compiler warnings will help to detect issues like this at compile time. I always use at least gcc -Wall -Wextra
(and -Wpedantic
too, but you can probably do without this). Enable compiler warnings, and pay attention to them, and fix them.