1

Why can not I print out the elements of an array in a for-loop? My environment is Windows 7 Maximum, gcc (MinGW.org GCC-6.3.0-1) 6.3.0 I input the data from the prompt like 3 1 2 3. I can do, e.g., printf("%" PRId64 " ", marray[2]) but from the for-loop it doesn't work.

Here is the source code:

#define __USE_MINGW_ANSI_STDIO 1
#define __STDC_FORMAT_MACROS 1

#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>

size_t read_size() {
    size_t x;
    scanf("%lu", &x);
    return x;
}

int64_t read_int64() {
    int64_t x;    
    scanf("%" SCNd64, &x);
    return x;
}

int main() {
    size_t *size;
    *size = read_size();
    int64_t *marray = malloc(sizeof(int64_t) * *size);
    for (size_t i = 0; i < *size; i++) {
        marray[i] = read_int64();
    }
        
    for (size_t i = 0; i < *size; i++) {
        printf("%" PRId64 " ", marray[i]);
    }
    free(marray);
    return 0;
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189
Penelopa
  • 13
  • 3
  • 1
    Unrelated to your problem but the correct [`scanf`](https://en.cppreference.com/w/c/io/fscanf) format to read `size_t` values is `%zu`. – Some programmer dude Feb 07 '22 at 14:38
  • 1
    `size` is uninitialized pointer, but you dereference it. Why is it a pointer at all? – Eugene Sh. Feb 07 '22 at 14:39
  • 1
    `size` is an uninitialized pointer to a `size_t`. The first time you dereference it, there will be blood. – Jeff Holt Feb 07 '22 at 14:39
  • 1
    You have a pointer `size` but you never make it point anywhere. That will lead to *undefined behavior* when you dereference the pointer. – Some programmer dude Feb 07 '22 at 14:39
  • Why has it become so common lately to see prototyped `void` parameters without including `void`? (eg `int64_t read_int64()` ) – ryyker Feb 07 '22 at 14:45
  • `size_t* size; *size = read_size();` -> `size_t size; size = read_size();`, All other `*size` -> `size`. – Jabberwocky Feb 07 '22 at 14:50
  • Thank you all guys, with size_t* size = NULL; it still doesn't work. – Penelopa Feb 07 '22 at 14:51
  • @Penelopa of course it doesn't work with `size_t* size = NULL;`. `size` still points nowhere. Read my previous comment. – Jabberwocky Feb 07 '22 at 14:52
  • I need this pointer for exercise purposes to use it later in another problem. – Penelopa Feb 07 '22 at 14:54
  • @Penelopa using size as a pointer is wrong. And for the other problem there will probably still be a problem when `size` is a pointer. Tell us more about that _"other problem"_. – Jabberwocky Feb 07 '22 at 14:55
  • @Jabberwocky, thank you, it works now, but i need a pointer for a later problem. – Penelopa Feb 07 '22 at 14:57
  • @Penelopa well, you need to tell us about the _"later problem"_. It's probably not what ou think it is, but wen can't help more here without further information. This question has been answered below. Maybe you should post another question about the specific _"later problem"_. – Jabberwocky Feb 07 '22 at 14:59
  • @Jabberwocky, wait, I dont undestand how to edit the text here in the comments ))) I am posting for the first time. This is the next learning problem: int64_t** marray_read( size_t* rows, size_t** sizes ) { *rows = read_size(); int64_t** marray = malloc(sizeof(int64_t*)**rows); *sizes = malloc(sizeof(size_t)**rows); for( size_t i = 0; i < *rows; i++ ) { marray[i] = array_int_read(*sizes+i); } return marray; } – Penelopa Feb 07 '22 at 15:04
  • @Penelopa don't post code in comments, as you can see, it's unreadable. Instead [edit] your question or better ask a new question alltogether, as this question has been answered. – Jabberwocky Feb 07 '22 at 15:05
  • Are you trying to create a 2D array? – ryyker Feb 07 '22 at 15:07
  • @Jabberwocky thank you, i see. I am a little bit overwhelmed now ))) I need time to process it all ))) – Penelopa Feb 07 '22 at 15:12
  • @ryyker, yes! It's a learning problem. – Penelopa Feb 07 '22 at 15:13
  • What are the two dimensions? – ryyker Feb 07 '22 at 15:15
  • See answer below for suggestions on 2D array. But keep in mind, if you are using `C99` or beyond, consider looking into [VLA](https://en.wikipedia.org/wiki/Variable-length_array). No `malloc()` calls required, and no `free()` calls either. – ryyker Feb 07 '22 at 15:32
  • @ryyker, thank you for your help! Only one addition, according to the problem statement I need to use a pointer size_t*. It will be used later. I only dont understand why I can not use *size in a for-loop for the second time, for printing. For example, if do: size_t p = *size-1; printf("%" PRId64 " ", marray[p]); it works perfectly. Why it doesn't work in a for-loop? – Penelopa Feb 07 '22 at 17:03
  • You _Can_ use a [dereferenced pointer](https://stackoverflow.com/questions/4955198/what-does-dereferencing-a-pointer-mean) in a `for()` loop, but typically it would be provided in the form of a dereferenced, initialized memory location (one that contains a real value.) . But In your code you do not have that. First you have: `size_t *size;`, creating an uninitialized pointer, then you call `*size = read_size();`, which will result in a run-time error when you attempt to dereference a null pointer. – ryyker Feb 07 '22 at 18:03
  • @Someprogrammerdude in my environment it only works with %lu not %zu. – Penelopa Feb 08 '22 at 12:57
  • @ryyker thank you again! But why then the first for-loop works with *size? – Penelopa Feb 08 '22 at 13:00
  • 1
    @Penelopa as we've already explained multiple times in comments and in the answer below, `size` is not initialized, it points __nowhere__. Therefore using `*size` results in undefined behaviour (google that term). Undefined behaviour includes "apparently working fine". I suggest you read again the chapter dealing with pointers in your learning material. – Jabberwocky Feb 08 '22 at 13:11
  • Regarding _"in my environment it only works with %lu not %zu."_: The "%zu" format specifier was added in the 1999 ISO C standard (and adopted by the 2011 ISO C++ standard). You must be using a compiler older than C99. Note, if your learning institution is forcing you to learn with old tools, change your learning institution for another one. If it is your choice, then [gcc is free](https://gcc.gnu.org/). – ryyker Feb 08 '22 at 13:41
  • 1
    @Jabberwocky thank you for explaning "undefined behaviour"! I have no specified learning material I am learning by myself. – Penelopa Feb 08 '22 at 16:47
  • 1
    @ryyker no requirements from the learning institution, its all my own choice, but I have a 32-bit system and I am contantly having troubles installing any soft. Thank you anyways! – Penelopa Feb 08 '22 at 16:50
  • I am sorry to hear that. Your efforts then are commendable. Have you tried `gcc`? ( here is a version that will [install on 32 bit machines](https://sourceforge.net/projects/tdm-gcc/) ) – ryyker Feb 08 '22 at 17:21
  • 1
    @ryyker thank you so much! I will try it! – Penelopa Feb 09 '22 at 20:15

1 Answers1

3

First, in main() you have:

size_t *size;  //creating an uninitialized pointer

then you call

*size = read_size();  
^ (dereferencing an uninitialized pointer

which will result in undefined behavior. (read about nasal demons here. Essentially anything can happen here. On my system, it was a run-time error. ( ...Local 'size' was referenced before being initialized. ) Note: If you do not see something similar, turn on your compiler warnings.

So, as pointed out in comments, using a pointer here is not the right approach anyway...

*size = read_size();

Suggest changing the following:

size_t read_size(){
    size_t x;
    scanf("%lu", &x);
    return x;
}

To:

size_t read_size(void){//note, its portable to include `void` in `void` prototypes
    size_t x;
    scanf("%zu", &x); //zu, not lu for size_t format specifier
    return x;
}

Then call it like this:

//size_t* size;
size_t size = read_size();

Then also change these :

int64_t* marray = malloc(sizeof(int64_t)**size);
...
for (size_t i = 0; i < *size; i++){

to:

int64_t* marray = malloc(sizeof(int64_t) * size);
if(marray)
{//always verify successful call before attempting to use memory
...
for (size_t i = 0; i < size; i++){//removed the dereference on size  

EDIT - comments indicate need to create 2D array dynamically:

comments indicate that your intention is that

int64_t* marray = malloc(sizeof(int64_t)**size);

creates a two dimension array. That statement will only create a single dimension array, and likely not of the size that you think.

In general, create a pointer to the array, then create pointers to the each column... (for example, without knowing both dimensions, the following will create both to be size)

int rows = size
int cols = size
int64_t **marray = malloc(sizeof(int64_t) * rows*cols);
if(!marray)//handle error
{
      for(int i = 0; i < cols ; i++)
      {
           marray[i] = malloc( rows * sizeof(int64_t));
           if(!marray[i]) //handle error;
      }
}

This method approximates the method indicated in comments, however IMO this is not the best way. It creates non-contiguous blocks of memory for each call to malloc, and requires one call to free() for each call to malloc(). A much better approach is here. (Uses only one call to malloc()/free() to create and free a 2D array.)

ryyker
  • 22,849
  • 3
  • 43
  • 87