I have already worked with the stack and the heap, and in the memory management topic generally, but there is a lot of thing's i can't understand
Like, if i'm allocating an array of integer using the heap with malloc and realloc how can i determine the exact size of the array i want to work with?
This Example:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[]){
int *array = (int *)malloc(2);
array[0] = 2;
array[1] = 1;
//What? i have allocated just 2 to be the size
array[2] = 3;
array[3] = 4;
array[4] = 4;
array[5] = 6;
//there is no segmentation fault
for(int i = 0; i < sizeof(array); i++){
printf("%d\n",array[i]);
}
}
And the wierd result i'm getting is:
2
1
3
4
4
6
1041 // ???
0
So, can someone explain to me how can i use malloc in the 100% correct way?
To correctly allocate an array using malloc, use sizeof to determine the size of each element in your array, then multiply by the number of each that you need.
Your code is only allocating 2 bytes of memory in heap, so when you write these integers (which take 4 bytes each on my machine), you are overwriting the values of unrelated state within the heap located beyond those two bytes, thus corrupting the machine state and creating undefined (that's bad) behavior.
In addition, your for loop was looping on the size of array pointer, which is typically 8 bytes. So your for loop would have tried to walk over 8 int elements, in an array of 6 ints in which you had only allocated 2 byte instead of the 24 bytes needed. Lots of bad undefined behaviour to go around here!
You may or may not get a segmentation fault due to this. A segmentation fault means you are dereferencing a pointer to an invalid page (segment) of memory, and this is caught by the hardware.
When you corrupt memory, you may not see the result of your error immediately as a segmentation fault if the memory you are writing is valid memory. Worse, if you corrupt the stack or a pointer, it may take a long time to get an actual fault to help detect the corruption created. This makes it hard to connect the fault to the event that caused the exception since your code could run for a long time before getting a segmentation fault.
#include <stdio.h>
#include <stdlib.h>
#define NUM_INTS_WANTED 6
int main(int argc, char* argv[]){
// No more trampling memory since array allocated to correct size
int *array = malloc(sizeof(int)*NUM_INTS_WANTED);
// Always check if malloc succeeded by checking that pointer is not NULL
if (array != NULL) {
array[0] = 2;
array[1] = 1;
array[2] = 3;
array[3] = 4;
array[4] = 4;
array[5] = 6;
// No more seeing 8 ints since you stop after 6 ints now
for(int i = 0; i < NUM_INTS_WANTED; i++){
printf("%d\n",array[i]);
}
} else {
// malloc failed! Report it.
printf("malloc failed!\n");
}
}
You can't. It's impossible.
Once upon a time a certain heap manager exposed this information by providing another function that would indeed return the real size. (It's padded up to the next block size.) They took it out after it was discovered to have caused more bugs than it prevented.
On most heap managers, the real size in bytes can be found at a small negative offset, but not all of them. Don't write this code. You will regret it if you have to maintain it. The heap manager can be swapped out from under you and you won't know what went wrong.
Related
#include <stdio.h>
int main()
{
int i,a;
int* p;
p=&a;
for(i=0;i<=10;i++)
{
*(p+i)=i;
printf("%d\n",*(p+i));
}
return 0;
}
I tried to assign numbers from 0 to 10 in a sequence memory location without using an array.
You are trying to write to memory that it does not have permission to access.
The variable a is a local variable in the main function, and it is stored on the stack. The pointer p is initialized to point to the address of a. The code then attempts to write to the memory addresses starting at p and going up to p+10. However, these memory addresses are not part of the memory that has been allocated for the program to use, and so the program receives a segmentation fault when it tries to write to them.
To fix this issue, you can either change the loop condition to a smaller value, or you can allocate memory dynamically using malloc or calloc, and assign the pointer to the returned address. This will allow you to write to the allocated memory without causing a segmentation fault.
Like this:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int i;
int* p = malloc(sizeof(int) * 11); // Allocate memory for 10 integers
if (p == NULL) { // Check for allocation failure
printf("Error allocating memory\n");
return 1;
}
for(i=0;i<=10;i++)
{
*(p+i)=i;
printf("%d\n",*(p+i));
}
free(p); // Free the allocated memory when you are done with it
return 0;
}
a is only an integer. not an array.
you need to declare it differently:
int i, a[10];
You can not. Memory of int is 4 bytes and you can store only single number in that memory.
for int: -2,147,483,647 to 2,147,483,647
for unsigned int: 0 to 4, 294, 967 295
There are other types you can use with different sizes, but if you want to put different numbers into one variable you need to use array.
int arr[10];
arr[0] = 0;
arr[1] = 5;
something like this.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
When I try to run the following logic snippits, I get segmentation faults, why? Sometimes I don't get segmentation faults but instead I get odd output that does not make sense...
1. Changing a string's character
char *str = "Hello!";
str[0] = 'h'; // SIGSEGV
2. Modifying the value of a pointer
int *num_ptr;
*num_ptr = 6; // SIGSEGV
3. Using an array or pointer returned from a function
int *getPoint()
{
int cords[2] = {10, 20};
return cords;
}
/* ... */
int *point = getPoint();
int total = point[0] + point[1]; // SIGSEGV (or sometimes total = 0?)
4. Looping through n-sized array in function
void print_loop(int *array)
{
for(int i = 0; i < sizeof(array); i++)
printf("array[%d] = %d\n", i, array[i]); // SIGSEGV
}
/* ... */
int nums[3] = {1, 2, 3};
print_loop(nums);
What do I do to fix each of these and understand why they happen?
char *str;
int *num_ptr;
int *point;
^^ this code just creates a variable for you to then use which is a pointer to memory. Realize it never allocates or reserves memory.
so for 2 where you do *num_ptr = 6; you are trying to put the value of 6 into memory pointed to by num_ptr. But num_ptr does not point anywhere yet, it either points to NULL or is uninitialized and contains some random number value. If it contains some random number value odds are it is not valid memory location, hence the segmentation violation. And if it were NOT to result in a SIGSEV and the code ran then it would be by chance you got lucky; you would never want to program this way having basic concepts of programming happen by chance.
Same principle applies for all your other examples. It doesn't matter the data type. It's the basic problem of having a pointer to memory, and whether or not you have (by whatever means) reserved or allocated some valid range of storage space (RAM, disk, wherever).
str[0] = 'h'; is the same thing as *str = 'h';
str[1] = 'h'; would be the equivalent of *(str+1) = 'h';
str[2] is *(str+2)
and so on,
where str is a pointer to the starting location of a range of memory,
then because str is of data type CHAR, +1 moves 1 byte, +2 moves 2 byte.
If it were of type INT where int is 4 bytes, then +1 would be the
initial memory location + 4.
Using an array or pointer returned from a function
This doesnt't work, because you create a pointer and reserve a memory area inside a function, without malloc. When this function ends, you lose this reserved area.
So, you can't make a function that returns a pointer this way. Read a little about malloc() function.
Looping through n-sized array in function
This isn't the right way to loop through a n-sized array, sizeof return the number of bytes that you need tho allocate that structure, not how many elements you have in the array. A suggestion is by as a parameter how many elements you have, if it is a string, you can use strlen, from string.h.
these are common pit falls when someone tries migrate to C from a virtual language such as Java, C#, Python, or JavaScript. The problem is that they do not understand the concept of memory management because virtual languages take care of that now. Hopefully new programmers wanting to learn C can be directed to this answer and avoid these pitfalls -- and ultimately prevent these types of questions from filling the front page of c every day. I am going to be short worded as possible, as I know that 90% of you have already looked at this paragraph and said "lol tl;dr".
What is a Segmentation Fault (seg fault)
It's when you try to access memory that's not yours. Or you try to write to memory that is read only -- The operating system is the one responsible for enforcing this.
An analogy: address in memory are like house address on the streets. And the things inside the houses would be the data itself. If you own a house, you go to its address and add and remove furniture as you please. If you don't own the house, you're simply breaking an entry and the police (The OS) arrest you (seg fault).
Common ways to get it
P1. Changing a string's character
char *str = "Hello!";
str[0] = 'h'; // SIGSEGV
Problem: a string literal (declared within quotes, "LIKE SO") may not be modified. Attempting to do so causes undefined behavior.
Solution: allocate your string in an array.
char str[] = "Hello!";
str[0] = 'h';
P2. Modifying the value of a pointer
int *num_ptr;
*num_ptr = 6; // SIGSEGV
Problem: num is pointer, but its address has not been defined. You can't derreference a pointer unless you give it a valid address.
Solution: Make sure you point your pointers®.
int *num_ptr;
int num;
num_ptr = # // num_ptr equals the address of num
*num_ptr = 6;
P3. Using an array or pointer returned from a function
int *getPoint()
{
int cords[2] = {10, 20};
return cords;
}
/* ... */
int *point = getPoint();
int total = point[0] + point[1]; // SIGSEGV (or sometimes total = 0?)
Problem: cords is an array with automatic storage inside the function getPoint(). When this function returns, the array no longer exists, which means that point now points to invalid memory.
Solution #1: allocate coords with dynamic memory using, for example, malloc(). Dynamic memory exists as long as your program is running, or until you release it with free().
int *getPoint()
{
int *cords = malloc(2 * sizeof(int)); // get 2 ints on permanent (heap) memory
cords[0] = 10;
cords[1] = 20;
return cords;
}
/* ... */
int *point = getPoint();
int total = point[0] + point[1];
free(point); // manual destroy
Solution #2: declare the array outside the function and pass its address to getPoint(). Now instead of returning the array, it receives and modifies it.
int *getPoint(int *cords)
{
cords[0] = 10;
cords[1] = 20;
return cords;
}
/* ... */
int cords[2];
int *point = getPoint(chords);
int total = point[0] + point[1]
P4. Looping through n-sized array in function
void print_loop(int *array)
{
for(int i = 0; i < sizeof(array); i++)
printf("array[%d] = %d\n", i, array[i]); // SIGSEGV
}
/* ... */
int nums[3] = {1, 2, 3};
print_loop(nums);
Problem: In C, there is nothing holding your hand. This means you have to keep track of your own damn array lengths.
Solution: Whenever you pass an array into a function, you MUST also pass the size of the array.
void print_loop(int *array, int array_len)
{
for(int i = 0; i < array_len; i++)
printf("array[%d] = %d\n", i, array[i]);
}
/* ... */
int nums[3] = {1, 2, 3};
print_loop(nums, 3);
"My program does not have a seg fault and/or but it gives a strange result"
Seg faults and things that should cause seg faults are known as "undefined behavior". This means that the error will be different based on what compiler is used, what OS, what CPU, ect. Some cases there will not be an error at all, however that does not mean its OK. It's like saying "If OJ can get away with it then so can I" and then being surprised by the results.
I allocate a 2D array of char * and every string length is 12.
50 rows and 2000000 columns.
Lets calculate it:
50*2000000 * (12(length)+8(for pointer)). I use 64 bit.
50*2000000 * 20 =2000000000 bits .. -> 2 GB.
When I check the memory monitor it shows that the process takes 4 GB.
(All that happened after allocation)
This is the code:
int col=2000000,row=50,i=0,j=0;
char *** arr;
arr=(char***)malloc(sizeof(char**)*row);
for(i=0;i<row;i++)
{
arr[i]=(char ** )malloc(sizeof(char*)*col);
for(j=0;j<col;j++)
{
arr[i][j]=(char*)malloc(12);
strcpy(arr[i][j],"12345678901");
arr[i][j][11]='\0';
}
}
May that be from the paging in Linux?
Each call of malloc is taking more memory than you ask. Malloc needs to store somewhere its internal info about allocated place, like size of allocated space, some info about neighbors chunks, etc. Also (very probably) each returned pointer is aligned to 16 bytes. In my estimation each allocation of 12 bytes takes 32 bytes of memory. If you want to save memory allocate all strings in one malloc and split them into sizes per 12 at your own.
Try the following:
int col=2000000,row=50,i=0,j=0;
char *** arr;
arr= malloc(sizeof(*arr)*row);
for(i=0;i<row;i++)
{
arr[i]= malloc(sizeof(*arr[i])*col);
char *colmem = malloc(12 * col);
for(j=0;j<col;j++)
{
arr[i][j] = colmem + j*12;
strcpy(arr[i][j],"12345678901");
}
}
I would re-write the code from scratch. For some reason, around 99% of all C programmers don't know how to correctly allocate true 2D arrays dynamically. I'm not even sure I'm one of the 1% who do, but lets give it a shot:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
int main()
{
const int COL_N = 2000000;
const int ROW_N = 50;
char (*arr)[ROW_N] = malloc( sizeof(char[COL_N][ROW_N]) );
if(arr == NULL)
{
printf("Out of memory");
return 0;
}
for(int row=0; row<ROW_N; row++)
{
strcpy(arr[row], "12345678901");
puts(arr[row]);
}
free(arr);
return 0;
}
The important parts here are:
You should always allocate multi-dimensional arrays in adjacent memory cells or they are not arrays, but rather pointer-based lookup tables. Thus you only need one single malloc call.
This should save a bit of memory since you only need one pointer and it is allocated on the stack. No pointers are allocated on the heap.
Casting the return value of malloc is pointless (but not dangerous on modern compilers).
Ensure that malloc actually worked, particularly when allocating ridiculous amounts of memory.
strcpy copies the null termination, you don't need to do it manually.
There is no need for nested loops. You want to allocate a 2D array, not a 3D one.
Always clean up your own mess with free(), even though the OS might do it for you.
I started to learn C recently. I use Code::Blocks with MinGW and Cygwin GCC.
I made a very simple prime sieve for Project Euler problem 10, which prints primes below a certain limit to stdout. It works fine until roughly 500000 as limit, but above that my minGW-compiled .exe crashes and the GCC-compiled one throws a "STATUS_STACK_OVERFLOW" exception.
I'm puzzled as to why, since the code is totally non-recursive, consisting of simple for loops.
#include <stdio.h>
#include <math.h>
#define LIMIT 550000
int main()
{
int sieve[LIMIT+1] = {0};
int i, n;
for (i = 2; i <= (int)floor(sqrt(LIMIT)); i++){
if (!sieve[i]){
printf("%d\n", i);
for (n = 2; n <= LIMIT/i; n++){
sieve[n*i] = 1;
}
}
}
for (i; i <= LIMIT; i++){
if (!sieve[i]){
printf("%d\n", i);
}
}
return 0;
}
Seems like you cannot allocate 550000 ints on the stack, allocate them dynamically instead.
int * sieve;
sieve = malloc(sizeof(int) * (LIMIT+1));
Your basic options are to store variables in data segment when your memory chunk is bigger than stack:
allocating memory for array in heap with malloc (as #Binyamin explained)
storing array in Data/BSS segments by declaring array as static int sieve[SIZE_MACRO]
All the memory in that program is allocated on the stack. When you increase the size of the array you increase the amount of space required on the stack. Eventually the method cannot be called as there isn't enough space on the stack to accomodate it.
Either experiement with mallocing the array (so it's allocated on the heap). Or learn how to tell the compiler to allocate a larger stack.
I'm stunned, why does this code give me a segmentation fault?
#include <stdio.h>
#define LIMIT 1500000
typedef struct {
int p;
int a;
int b;
} triplet;
int main(int argc, char **argv) {
int i;
triplet triplets[LIMIT];
for (i = 0; i < LIMIT; i++) {
triplets[i].p = 9; // remove this line and everything works fine
}
printf("%d\n", triplets[15].p);
return 0;
}
EDIT: After changing LIMIT to 150 I no longer get a segmentation fault, it prints random numbers instead.
EDIT2: Now I know what the site name stands for :) I made the array global and everything works fine now.
Stack overflow! Allocating 1500000 records at 12 bytes per record (assuming 4-byte int), requires more than 17 MB of stack space. Make your triplets array global or dynamically allocate it.
As to your edit - shrinking the array will probably stop the stack overflow, but your printf() call will still print uninitialized data - triplets[15].p could be anything at the time you print it out.
When you do
triplet triplets[LIMIT];
you're allocating that on the stack. Which is apparently too big for your system.
If you do
triplet* triplets=(triplet*)malloc(LIMIT*sizeof(triplet));
you'll allocate it on the heap and everything should be fine. Be sure to free the memory when you're done with it
free(triplets);