Accessing members of a passed struct array in C - c

I have a method levenshtein that populates a 2D array of structs w/ info and returns a pointer to that array. When I send it to another method, I get a Segmentation Fault (core dumped) error at runtime. Please help me w/ this hopefully obvious error.
struct chartEntry
{
int num;
bool left,
up,
diag;
};
struct chartEntry** levenshtein(char *s1, char *s2, bool toPrint)
{
unsigned int s1Len,
s2Len,
i, //rows, general purpose index
j; //columns, general purpose index
s1Len = strlen(s1);
s2Len = strlen(s2);
/***********************************
Create and populate traceback chart
***********************************/
struct chartEntry chart [s1Len+1][s2Len+1];
//
// code to populate chart here
//
//prints expected number
printf("chart[3][3].num is %d", chart[3][3].num);
return chart;
}
void testFunction(char*s1,char*s2)
{
// both of these give segmentation faults
printf("[3][3].num is %d", levenshtein(s1,s2,false)[3][3].num);
struct chartEntry ** tmp = levenshtein(s1,s2,false);
printf("[3][3].num is %d", tmp[3][3].num);
}

There's 2 problems here. Firstly, chart is an array of arrays. This cannot be converted to a pointer-to-a-pointer. Arrays and pointers are different. Your line return chart; must give you a compilation error which you should not ignore.
Secondly, even if it could be converted, chart's memory is local to the levenshtein function and will no longer exist when that function returns. So you would be returning a wild pointer.
You have two options:
allocate memory for chart using malloc and return a pointer to that (either one large block, or with two levels of indirection)
have the caller allocate the array and the levenshtein function just writes values into it.
If you use the first option then you should not use the printf as you have done the first time in testFunction, because the memory would not be freed. You have to save the returned pointer, printf it, and then execute a sequence of free that is the reverse of the malloc sequence you used to allocate it.

In your code, you're trying to return a pointer to an array that's declared inside the function:
struct chartEntry** levenshtein(char *s1, char *s2, bool toPrint)
{
// ...
struct chartEntry chart [s1Len+1][s2Len+1];
// ...
return chart;
}
However, as soon as the program exits this function, this array falls out of scope and is destroyed, leaving you with a pointer pointing to invalid memory. This leads to undefined behavior: Maybe it works, maybe it doesn't, maybe it puts your computer on fire. Okay, that last one is pretty unlikely, but the point is, anything can happen.
There are two ways to deal with this problem:
Create a static array before calling the function, and then pass a pointer to this array in the function call.
Dynamically allocate memory in the function, which can then be returned as a normal pointer. (The calling function has to make sure the memory is freed after use, though, otherwise this may lead to memory leaks, which is a bad, bad thing.)

Related

Returning an array from function in C

I've written a function that returns an array whilst I know that I should return a dynamically allocated pointer instead, but still I wanted to know what happens when I am returning an array declared locally inside a function (without declaring it as static), and I got surprised when I noticed that the memory of the internal array in my function wasn't deallocated, and I got my array back to main.
The main:
int main()
{
int* arr_p;
arr_p = demo(10);
return 0;
}
And the function:
int* demo(int i)
{
int arr[10] = { 0 };
for (int i = 0; i < 10; i++)
{
arr[i] = i;
}
return arr;
}
When I dereference arr_p I can see the 0-9 integers set in the demo function.
Two questions:
How come when I examined arr_p I saw that its address is the same as arr which is in the demo function?
How come demo_p is pointing to data which is not deallocated (the 0-9 numbers) already in demo? I expected that arr inside demo will be deallocated as we got out of demo scope.
One of the things you have to be careful of when programming is to pay good attention to what the rules say, and not just to what seems to work. The rules say you're not supposed to return a pointer to a locally-allocated array, and that's a real, true rule.
If you don't get an error when you write a program that returns a pointer to a locally-allocated array, that doesn't mean it was okay. (Although, it means you really ought to get a newer compiler, because any decent, modern compiler will warn about this.)
If you write a program that returns a pointer to a locally-allocated array and it seems to work, that doesn't mean it was okay, either. Be really careful about this: In general, in programming, but especially in C, seeming to work is not proof that your program is okay. What you really want is for your program to work for the right reasons.
Suppose you rent an apartment. Suppose, when your lease is up, and you move out, your landlord does not collect your key from you, but does not change the lock, either. Suppose, a few days later, you realize you forgot something in the back of one closet. Suppose, without asking, you sneak back to try to collect it. What happens next?
As it happens, your key still works in the lock. Is this a total surprise, or mildly unexpected, or guaranteed to work?
As it happens, your forgotten item still is in the closet. It has not yet been cleared out. Is this a total surprise, or mildly unexpected, or guaranteed to happen?
In the end, neither your old landlord, nor the police, accost you for this act of trespass. Once more, is this a total surprise, or mildly unexpected, or just about completely expected?
What you need to know is that, in C, reusing memory you're no longer allowed to use is just about exactly analogous to sneaking back in to an apartment you're no longer renting. It might work, or it might not. Your stuff might still be there, or it might not. You might get in trouble, or you might not. There's no way to predict what will happen, and there's no (valid) conclusion you can draw from whatever does or doesn't happen.
Returning to your program: local variables like arr are usually stored on the call stack, meaning they're still there even after the function returns, and probably won't be overwritten until the next function gets called and uses that zone on the stack for its own purposes (and maybe not even then). So if you return a pointer to locally-allocated memory, and dereference that pointer right away (before calling any other function), it's at least somewhat likely to "work". This is, again, analogous to the apartment situation: if no one else has moved in yet, it's likely that your forgotten item will still be there. But it's obviously not something you can ever depend on.
arr is a local variable in demo that will get destroyed when you return from the function. Since you return a pointer to that variable, the pointer is said to be dangling. Dereferencing the pointer makes your program have undefined behavior.
One way to fix it is to malloc (memory allocate) the memory you need.
Example:
#include <stdio.h>
#include <stdlib.h>
int* demo(int n) {
int* arr = malloc(sizeof(*arr) * n); // allocate
for (int i = 0; i < n; i++) {
arr[i] = i;
}
return arr;
}
int main() {
int* arr_p;
arr_p = demo(10);
printf("%d\n", arr_p[9]);
free(arr_p) // free the allocated memory
}
Output:
9
How come demo_p is pointing to data which is not deallocated (the 0-9 numbers) already in demo? I expected that arr inside demo will be deallocated as we got out of demo scope.
The life of the arr object has ended and reading the memory addresses previously occupied by arr makes your program have undefined behavior. You may be able to see the old data or the program may crash - or do something completely different. Anything can happen.
… I noticed that the memory of the internal array in my function wasn't deallocated…
Deallocation of memory is not something you can notice or observe, except by looking at the data that records memory reservations (in this case, the stack pointer). When memory is reserved or released, that is just a bookkeeping process about what memory is available or not available. Releasing memory does not necessarily erase memory or immediately reuse it for another purpose. Looking at the memory does not necessarily tell you whether it is in use or not.
When int arr[10] = { 0 }; appears inside a function, it defines an array that is allocated automatically when the function starts executing (or at certain times within the function execution if the definition is in some nested scope). This is commonly done by adjusting the stack pointer. In common systems, programs have a region of memory called the stack, and a stack pointer contains an address that marks the end of the portion of the stack that is currently reserved for use. When a function starts executing, the stack pointer is changed to reserve more memory for that function’s data. When execution of the function ends, the stack pointer is changed to release that memory.
If you keep a pointer to that memory (how you can do that is another matter, discussed below), you will not “notice” or “observe” any change to that memory immediately after the function returns. That is why you see the value of arr_p is the address that arr had, and it is why you see the old data in that memory.
If you call some other function, the stack pointer will be adjusted for the new function, that function will generally use the memory for its own purposes, and then the contents of that memory will have changed. The data you had in arr will be gone. A common example of this that beginners happen across is:
int main(void)
{
int *p = demo(10);
// p points to where arr started, and arr’s data is still there.
printf("arr[3] = %d.\n", p[3]);
// To execute this call, the program loads data from p[3]. Since it has
// not changed, 3 is loaded. This is passed to printf.
// Then printf prints “arr[3] = 3.\n”. In doing this, it uses memory
// on the stack. This changes the data in the memory that p points to.
printf("arr[3] = %d.\n", p[3]);
// When we try the same call again, the program loads data from p[3],
// but it has been changed, so something different is printed. Two
// different things are printed by the same printf statement even
// though there is no visible code changing p[3].
}
Going back to how you can have a copy of a pointer to memory, compilers follow rules that are specified abstractly in the C standard. The C standard defines an abstract lifetime of the array arr in demo and says that lifetime ends when the function returns. It further says the value of a pointer becomes indeterminate when the lifetime of the object it points to ends.
If your compiler is simplistically generating code, as it does when you compile using GCC with -O0 to turn off optimization, it typically keeps the address in p and you will see the behaviors described above. But, if you turn optimization on and compile more complicated programs, the compiler seeks to optimize the code it generates. Instead of mechanically generating assembly code, it tries to find the “best” code that performs the defined behavior of your program. If you use a pointer with indeterminate value or try to access an object whose lifetime has ended, there is no defined behavior of your program, so optimization by the compiler can produce results that are unexpected by new programmers.
As you know dear, the existence of a variable declared in the local function is within that local scope only. Once the required task is done the function terminates and the local variable is destroyed afterwards. As you are trying to return a pointer from demo() function ,but the thing is the array to which the pointer points to will get destroyed once we come out of demo(). So indeed you are trying to return a dangling pointer which is pointing to de-allocated memory. But our rule suggests us to avoid dangling pointer at any cost.
So you can avoid it by re-initializing it after freeing memory using free(). Either you can also allocate some contiguous block of memory using malloc() or you can declare your array in demo() as static array. This will store the allocated memory constant also when the local function exits successfully.
Thank You Dear..
#include<stdio.h>
#define N 10
int demo();
int main()
{
int* arr_p;
arr_p = demo();
printf("%d\n", *(arr_p+3));
}
int* demo()
{
static int arr[N];
for(i=0;i<N;i++)
{
arr[i] = i;
}
return arr;
}
OUTPUT : 3
Or you can also write as......
#include <stdio.h>
#include <stdlib.h>
#define N 10
int* demo() {
int* arr = (int*)malloc(sizeof(arr) * N);
for(int i = 0; i < N; i++)
{
arr[i]=i;
}
return arr;
}
int main()
{
int* arr_p;
arr_p = demo();
printf("%d\n", *(arr_p+3));
free(arr_p);
return 0;
}
OUTPUT : 3
Had the similar situation when i have been trying to return char array from the function. But i always needed an array of a fixed size.
Solved this by declaring a struct with a fixed size char array in it and returning that struct from the function:
#include <time.h>
typedef struct TimeStamp
{
char Char[9];
} TimeStamp;
TimeStamp GetTimeStamp()
{
time_t CurrentCalendarTime;
time(&CurrentCalendarTime);
struct tm* LocalTime = localtime(&CurrentCalendarTime);
TimeStamp Time = { 0 };
strftime(Time.Char, 9, "%H:%M:%S", LocalTime);
return Time;
}

When writing a function that returns pointer, why should I allocate memory to the pointer I'm going to return?

I'm a bit weak when it comes to memory allocation and pointers.
So, I want to understand why do I have to allocate memory to pointers in functions as follow:
char *cstring(char c, int n)
{
int i = 0;
char * res;
res = malloc ((n+1)*sizeof(char));
while (i<n)
{
res[i]=c;
i++;
}
res[i] ='\0';
return res;
}
and why is the following not valid?
char *cstring(char c, int n)
{
int i = 0;
char * res;
while (i<n)
{
res[i]=c;
i++;
}
res[i] ='\0';
return res;
}
I understand that I should allocate memory (generally) to pointers so that they have defined memory.
However, I want to mainly understand how is it related to the concept of stack and heap memories!
Thanks in advance!
Pointers need to point to a valid memory location before they can be dereferenced.
In your first example, res is made to point at a block of allocated memory which can subsequently be written to and read from.
In your second example, res remains uninitialized when you attempt to dereference it. This causes undefined behavior. The most likely outcome in this case is that whatever garbage value it happens to contain will not be a valid memory address, so when you attempt to dereference that invalid address your program will crash.
If you declare a variable like that,
int A = 5;
then that means the variable will be on the stack. When functions are called, their local variables are pushed to the stack. The main function is also an example of that. So you don't have to allocate memory manually, your compiler will do this for you in the background before it calls your main function. And that also means if you examine the stack during the execution of the function you can see the value 5.
With this,
int A = 5;
int *PtrToA = &A;
The pointer will be on the stack again. But this time, the value on the stack just shows the memory address of the actual integer value we want. It points to the address of the memory block that holds the value 5. Since A is held in the stack here, pointer will show a memory address on the stack.
Like the case in your question you can allocate memory dynamically. But you have to initialize it before you read it. Because when you request to allocate the memory, your operating system searches for a valid memory field in your programs heap and reserves that for you. Than it gives you back its adddress and gives you the read write permissions so you can use it. But the values in it won't contain what you want. When compiler allocates on stack, the initial values will be unset again. If you do this,
char *res;
res[1] = 3;
variable res will be on the stack and it will contain some random value. So accessing it is just like that,
(rand())[1] = 3;
You can get an access violation error because you may not have permission to write to that memory location.
An important note; after your function call returns, values of local variables on the stack are no more valid. So be careful with that. Do not dereference them after the function call ends.
In conclusion; if you want to use a pointer, be sure it points to a valid memory location. You can allocate it yourself or make it point another memory address.
The second version of your code declares a pointer, but does not initialize it to point to a valid memory address.
Then, the code dereferences that pointer -- during the loop. So, your code would access uninitialized memory in this case. Remember, array indexing is just syntactic sugar for dereferencing -- so your code accesses memory its not supposed to.
The first version of your code initializes the pointer to actually point to something, and hence when you dereference it during the loop, it works.
Of course, in either case, you return the pointer from the function -- its just that in the first version it points to something valid, whereas in the second version it points anywhere.
The moral here is to always initialize your variables. Not doing so could result in undefined behavior in your code (even if it appears to work sometimes). The general advice here is to always compile your code using at least some compilation flags. For example in gcc/clang, consider -Wall -Werror -Wextra. Such options often pick up on simple cases of not initializing variables.
Also, valgrind is a brilliant tool for memory profiling. It can easily detect uses of uninitialized memory at runtime, and also memory leaks.
Simple: because you do not have any allocated memory for the data you wite. In your example you define pointer, you do not initialize it so it will reference random (or rather not possible to predict) place in the memory, then you try to write to this random memory location.
You have 2 Undefined Behaviours here in 5 lines example. Your pointer is not initialized, and you did not allocate any valid memory this pointer to reference.
EDIT:
VALID
char *cstring(char c, int n)
{
char * res;
res = malloc ((n+1)*sizeof(char));
char *cstring(char c, int n)
{
char * res;
static char buff[somesize];
res = buff;
char buff[somesize];
char *cstring(char c, int n)
{
char * res;
res = buff;
INVALID
char *cstring(char c, int n)
{
char * res;
char buff[somesize];
res = buff;

Returning an array of variable size in c from a function

So i want to return an array of a size n (variable) which my function has as input. I know that in order to return arrays in C I have to define them static, but the problem is that n is a variable and thus I get an error. I thought of actually using malloc/calloc but then I won't be able to free them after the function returns the array. Please take note that I'm not allowed to change anything on main(). Are there any other alternatives which I could use? Thanks in advance.
float *Arr( int *a , int n ){
static float b[ n ];
return b
}
Got to point out that the function will only be called Once,I saw the solution you posted but i noticed you aren't freeing the allocated memory,is it not of much importance when the malloc is called inside a function?
The important thing to notice here is that this syntax:
float arr[n];
Allocates an array on the stack of the current function. In other words, that array is a local variable. Any local variable becomes invalid after the function returns, and therefore returning the array directly is undefined behavior. It will most likely cause a crash when trying to access the array from outside the function, if not anything worse.
In addition to that, declaring a variable-length array as static is invalid in any case.
If you want to write a function which creates and returns any kind of array (dynamically sized or not), the only option you have is to use dynamic allocation through malloc() and then return a pointer to the array (technically there's also alloca() to make dynamic stack allocations, but I would avoid it as it can easily break your program if the allocation is too large).
Here's an example of correct code:
float *create_array(size_t n_elements){
float *arr = malloc(sizeof(float) * n_elements);
if (arr == NULL) {
// Memory could not be allocated, handle the error appropriately.
}
return arr;
}
In this case, malloc() is reserving memory outside of the local stack of the function, in the heap. The result is a pointer that can be freely returned and passed around without any problem, since that area of memory keeps being valid after the function returns (until it is released). When you're done working with the data, you can release the allocated memory by calling free():
float *arr = create_array(100);
// ...
free(arr);
If you don't have a way to release the memory through free() after using malloc(), that's a problem in the long run, but in general, it is not a strict requirement: if your array is always needed, from its creation until the exit of the program, then there's no need to explicitly free() it, since memory is automatically released when the program terminates.
If your function needs to be called more than once or needs to create significantly sized arrays that are only useful in part of the program and should therefore be discarded when no longer in use, then I'm afraid there's no good way of doing it. You should use free() in that case.
To answer your question precisely:
Please take note that I'm not allowed to change anything on main(). Are there any other alternatives which I could use?
No, there are no other better alternatives. The only correct approach here is to dynamically allocate the array through malloc(). The fact that you cannot free it afterwards is a different kind of problem.
So i want to return an array of a size n(variable) which my function
has as input,
You can't, because C functions cannot return arrays at all. They can, and some do, return pointers, however, as your function is declared to do. Such a pointer may point to an element of an array.
i know that in order to return arrays in c i have to
define them static,
As long as I am being pedantic, the problem is to do with the lifetime of the object to which the returned pointer points. If it is an element of an automatically-allocated array, then it, along with the rest of the array, ceases to exist when the function returns. The caller must not try to dereference such a pointer.
The two other alternatives are
static allocation, which you get by declaring the variable static or by declaring it at file scope, and
dynamic allocation, which you get by reserving memory via malloc(), calloc(), or a related function.
Statically allocated objects exist for the entire lifetime of the program, and dynamically allocated ones exist until deallocated.
but problem is that n is a variable and thus i get
an error.
Yes, because variable-length arrays must be automatically allocated. Static objects exist for the whole run of the program, so the compiler needs to reserve space for them at compile time.
I thought of actually using malloc/calloc but then i won't be
able to free them after the function returns the array.
That's correct, but dynamic allocation is still probably the best solution. It is not unreasonable for a called function to return a pointer to an allocated object, thus putting the responsibility on its caller to free that object. Ordinarily, that would be a well-documented characteristic of the function, so that its callers know that such responsibility comes with calling the function.
Moreover, although it's a bit untidy, if your function is to be called only once then it may be acceptable to just allow the program to terminate without freeing the array. The host operating system can generally be relied upon to clean up the mess.
Please take
note that im not allowed to change anything on main(),are there any
other alternatives which i could use?
If you have or can impose a bound on the maximum value of n then you can declare a static array of that maximum size or longer, and return a pointer to that. The caller is receiving a pointer, remember, not an array, so it can't tell how long the pointed-to array actually is. All it knows is that the function promises n accessible elements.
Note well that there is a crucial difference between the dynamic allocation and static allocation alternatives: in the latter case, the function returns a pointer to the same array on every call. This is not inherently wrong, but it can be problematic. If implemented, it is a characteristic of the function that should be both intentional and well-documented.
If want an array of n floats where n is dynamic, you can either create a
variadic-length array (VLA):
void some_function(...)
{
//...
float b[ n ]; //allocate b on the stack
//...
}
in which case there would be no function call for the allocation, or you can allocate it dynamically, e.g., with malloc or calloc, and then free it after you're done with it.
float *b = malloc(sizeof(*b)*n);
A dynamic (malloc/calloc) allocation may be wrapped in a function that returns a pointer to the allocated memory (the wrapper may do some initializations on the allocated memory after the memory has been successfully allocated). A VLA allocation may not, because a VLA ends its lifetime at the end of its nearest enclosing block (C11 Standard - 6.2.4 Storage durations of objects(p7)).
If you do end up wrapping a malloc/calloc call in a "constructor" function like your float *Arr(void), then you obviously should not free the to-be-returned allocated memory inside Arr–Arr's caller would be responsible for freeing the result (unless it passed the responsibility over to some other part of the program):
float *Arr( int n, ...
/*some params to maybe initialize the array with ?*/ )
{
float *r; if (!(r=malloc(sizeof(*r)*n)) return NULL;
//...
//do some initializations on r
//...
return r; //the caller should free it
}
you could use malloc to reserve memory for your n sized array
Like this:
#include <stdlib.h>
#include <stdio.h>
float * arr(int * a, int n ) {
float *fp = malloc ( (size_t) sizeof(float)*n);
if (!fp) printf("Oh no! Run out of memory\n");
return fp;
}
int main () {
int i;
float * fpp = arr(&i,200);
printf("the float array is located at %p in memory\n", fpp);
return(0);
}
It seems like what you want to do is:
have a function that provides (space for) an array with a variable number of elements,
that the caller is not responsible for freeing,
that there only needs to be one instance of at a time.
In this case, instead of attempting to define a static array, you can use a static pointer to manage memory allocated and freed with realloc as needed to adjust the size, as shown in the code below. This will leave one instance in existence at all times after the first call, but so would a static array.
This might not be a good design (it depends on circumstances not stated in the question), but it seems to match what was requested.
#include <stdio.h>
#include <stdlib.h>
float *Arr(int *a , int n)
{
// Keep a static pointer to memory, with no memory allocated initially.
static float *b = NULL;
/* When we want n elements, use realloc to release the old memory, if any,
and allocate new memory.
*/
float *t = realloc(b, n * sizeof *t);
// Fail if the memory allocation failed.
if (!t)
{
fprintf(stderr, "Error, failed to allocate memory in Arr.\n");
exit(EXIT_FAILURE);
}
// Return the new memory.
return b;
}

Union as a Structure Variable in C

I have the following structures:
complex_attribute
generic_attribute
I defined a union of these two, like so:
union union_attribute{
struct complex_attribute *complex;
struct generic_attribute *generic;
};
And then i defined another structure which keeps track of the union and a code associated with it:
struct tagged_attribute{
int code;
union union_attribute *attribute;
};
Then, i defined another structure called Disk, which contains an array of pointers to tagger_attribute objects:
struct disk {
struct tagged_attribute *attribute_ptr[100];
}
Now I am trying to access the code of the tagged attribute like this:
printf("%i", disk_ptr->attribute_ptr[counter]->code);
But I am getting a segmentation fault. Is my way of accessing the structure variable "code" incorrect?
Here is all of the relevant code from where I am trying to access "code":
struct disk* construct_disk(char* name, char* serial, char* vendor, char* model, int RPM, char *raid_type, int num_args, ...){
struct disk *disk_ptr;
disk_ptr = malloc (sizeof (struct disk));
va_list ap;
va_start(ap, num_args);
int counter;
int incrementer;
//subattributes is a global variable
incrementer = subattributes[counter];
for(counter = 0; counter < num_attributes; counter++, incrementer = subattributes[counter]){
printf("Counter = %i\n", counter);
printf("incrementer = %i\n", incrementer);
if (1){
printf("Populating generic attribute");
printf("%i", disk_ptr->attribute_ptr[counter]->code);
//disk_ptr->attribute_ptr[counter]->code = GENERIC_ATTRIBUTE_TYPE;
//disk_ptr->attribute_ptr[counter]->attribute->generic = construct_generic_attribute(va_arg(ap, int));
}else{
printf("Populating complex attribute");
//struct generic_attribute* input_to_complex_attribute[incrementer];
//int stepper;
//for(stepper = 0; stepper<incrementer; stepper++){
// input_to_complex_attribute[stepper] = construct_generic_attribute(va_arg(ap, int));
//}
//disk_ptr->attribute_ptr[counter]->code = COMPLEX_ATTRIBUTE_TYPE;
//disk_ptr->attribute_ptr[counter]->attribute->complex = construct_complex_attribute(5, incrementer, input_to_complex_attribute);
}
}
va_end(ap);
return disk_ptr;
}
You aren't accessing code at all (which you should be using to check which union member is valid, of course).
You're accessing the counterth element of the attribute_ptr array, which is a pointer to a tagged_attribute and trying to dereference it (with ->). Possibly without allocating that pointer (or any of the others in that array) first, nor initialising the memory after allocation (you haven't shown anything about that... failure to allocate correctly is a likely cause of your segfault.)
Of course, this assumes disk_ptr has been allocated and initialised correctly... which you haven't shown and may not have.
As already mentioned, show all relevant code if you want a more specific answer to your question. Further, compile with warnings enabled and learn to use tools such as gdb (GNU debugger) and valgrind (for memory issues) to debug your code.
EDIT: Now that you've added code, you have allocated disk_ptr but you never allocate anything in attribute_ptr, so it's just 100 pointers pointing to arbitrary places in memory.
You are dereferencing disk_ptr->attribute_ptr[1], but that array of pointers is not initialized (the pointers point nowhere).
You have to malloc the struct tagged_attribute entries for each pointer first.
In C if a pointer is not pointing to valid memory you will have undefined behavior. One of the common things that happens is a segmentation fault as the pointer has random chunk or was initialized to zero and when you try to access the memory it is pointing to the hardware detects that you are accessing an invalid memory page. Every time you use -> you are dereferencing the pointer and run the risk of a seg fault. You should use a debugger to find the incorrect value. An alternative is to print the values before you dereference them:
printf("disk_ptr = %p\n", disk_ptr);
printf("disk_ptr->attribute_ptr[counter] = %p\n", disk_ptr->attribute_ptr[counter]);
You should place this code before the print that you shows. If disk_ptr is an invalid value the second print will fail. The first print will always succeed but you should see if it is NULL or not. If you do not see the second print that means the disk_ptr is an invalid pointer. This because in the second print disk_ptr is dereferenced via the -> operator and if it points to chunk it could (let me emphasize could) cause a seg fault (it could also overwrite some other data which might cause a seg fault later). If the second print works but the print you shows does not then disk_ptr->attribute_ptr[counter] could be the invalid pointer. Let me emphasize again because it is important. If a pointer is not pointing to a correct memory location you have undefined behavior. Dereferencing that pointer could cause a seg fault right there or could modify memory in some way such that a seg fault occurs later.

C - array of strings scope difference

I practiced an array of strings with no initial values.
Attempt 1
#include <stdio.h>
char *array[] = {};
int main(int argc, char *argv[]) {
array[0]="Hello";
array[1]="World";
char **i = array;
while (*i) {
printf("%d %s\n", i, *i);
i++;
}
}
$ gcc array_of_strings.c && ./a.out
6293704 Hello
6293712 World
It works fine.
Attempt 2
I thought I could move array pointer inside main function.
#include <stdio.h>
int main(int argc, char *argv[]) {
char *array[] = {};
array[0]="Hello";
array[1]="World";
char **i = array;
while (*i) {
printf("%d %s\n", i, *i);
i++;
}
}
$ gcc array_of_strings.c && ./a.out
-1899140568 (j͎?
-1899140560 World
-1899140552 ???%Y
-1899140544 1?I??^H??H???PTI???#
-1899140536 d?͎?
Segmentation fault
Huh, why it is not working? It causes "Segmentation fault" with ugly output.
Could somebody explain why I should not do this way?
You allocate an array with zero elements and then add two pointers to it. This writes outside of the array and causes a buffer overflow.
Incidentally, this overwrites unused memory if the array is allocated globally, but it overwrites the stack when it is allocated within main().
Two problems.
You aren't allocating space for the array items. With your empty initializer list you are allocating an empty array. When you write to array[0] and array[1] you are writing to memory you do not own.
You are getting lucky when you allocate the array globally. Global (aka statically-allocated) memory blocks tend to be filled with zeros. This is good for you because your while loop depends on their being a NULL pointer at the end of the array.
When you allocate on the stack and access memory past the end of the array you will get whatever happens to already be on the stack, which can be any arbitrary garbage. Your while (*i) loop doesn't get the NULL pointer it expects so it will keep on reading the garbage data until it finds some zeros that look like a NULL pointer.
To fix #1, give an explicit length to the array. To fix #2, you must explicitly add a NULL pointer to the end of the array.
char *array[3];
array[0]="Hello";
array[1]="World";
array[2]=NULL;
Also, for what it's worth, pointers aren't guaranteed to be the same size as ints. It is better to use %p to print pointers rather than %d.
printf("%p %s\n", i, *i);
In each of these cases, you are allocating an empty array, and then trying to insert items into it. C doesn't do any sort of resizing of arrays; if you insert items into an array beyond its length, it will just start overwriting any other data that might happen to come after the array. In your first case, when the array is a global, you manage to get lucky and apparently aren't breaking anything by writing past the end of the array, and furthermore are lucky that there is a null value just past the two that you insert, so your loop terminates at an appropriate place. In your second case, you happen to be overwriting your stack, which is used for storing local variables, passing arguments, return values, and return locations between functions. Thus, writing, and later reading, past the end of your array, is causing you to write all over your stack, and read random meaningless values.
Doing it on the second attempt declare the array on the stack and you then override arguments passed and at some point the return address at the end of the function.
The fact that it works in the first attempt is purely coincidental. You're still overriding memory you shouldn't but it currently does no harm.
You should not do it in both ways. In the first case, you were just lucky that you didn't overwrite some process-critical memory, in the second case you smashed the stack. Both cases might crash randomly because you are writing to memory that you did not reserve.
char *array[] = {};
This does reserve memory for zero entries, but with [0] = ... you're writing an element to a position for which you didn't allocate memory. You should read up about how to 1) define static arrays or 2) dynamically allocate arrays.

Resources