c - dereferencing issue - c

I have simplified an issue that I've been having trying to isolate the problem, but it is not helping.
I have a 2 dimensional char array to represent memory. I want to pass a reference to that simulation of memory to a function. In the function to test the contents of the memory I just want to iterate through the memory and print out the contents on each row.
The program prints out the first row and then I get seg fault.
My program is as follows:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
void test_memory(char*** memory_ref) {
int i;
for(i = 0; i < 3; i++) {
printf("%s\n", *memory_ref[i]);
}
}
int main() {
char** memory;
int i;
memory = calloc(sizeof(char*), 20);
for(i = 0; i < 20; i++) {
memory[i] = calloc(sizeof(char), 33);
}
memory[0] = "Mem 0";
memory[1] = "Mem 1";
memory[2] = "Mem 2";
printf("memory[1] = %s\n", memory[1]);
test_memory(&memory);
return 0;
}
This gives me the output:
memory[1] = Mem 1
Mem 0
Segmentation fault
If I change the program and create a local version of the memory in the function by dereferencing the memory_ref, then I get the right output:
So:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
void test_memory(char*** memory_ref) {
char** memory = *memory_ref;
int i;
for(i = 0; i < 3; i++) {
printf("%s\n", memory[i]);
}
}
int main() {
char** memory;
int i;
memory = calloc(sizeof(char*), 20);
for(i = 0; i < 20; i++) {
memory[i] = calloc(sizeof(char), 33);
}
memory[0] = "Mem 0";
memory[1] = "Mem 1";
memory[2] = "Mem 2";
printf("memory[1] = %s\n", memory[1]);
test_memory(&memory);
return 0;
}
gives me the following output:
memory[1] = Mem 1
Mem 0
Mem 1
Mem 2
which is what I want, but making a local version of the memory is useless because I need to be able to change the values of the original memory from the function which I can only do by dereferencing the pointer to the original 2d char array.
I don't understand why I should get a seg fault on the second time round, and I'd be grateful for any advice.
Many thanks
Joe

Try:
printf("%s\n", (*memory_ref)[i]);
The current version is equivalent to
*(*(memory_ref + i));
This is because the [] operator has higher precedence than the dereference *. Which means that when i is larger than 0 you try to read memory after the char*** temporary memory_ref
The second version is equal to (*memory_ref)[i] which means that you will be indexing the correct memory.
EDIT: The reason it works on the first iteration is because:
*(*(memory_ref + 0)) == *((*memory_ref) + 0)

This looks like a precedence issue. The [] is getting evaluated first, and the * second. What you would need is something like the following for your first code example ...
printf("%s\n", (*memory_ref)[i]);

The quick fix is to use
printf("%s\n", (*memory_ref)[i]);
But I suspect you have an problem in the lines
memory[0] = "Mem 0";
These do not copy the string "Mem 0" into your memory array. They make memory[i] point to the string.
You need to explicitly copy the data using strncpy
e.g.
char s = "Mem 0";
strncpy( memory1, s, max( strlen(s) + 1, 29) ) // max of 30= (29 char + '\0' as that is the row length you allocated - better to #define this as a constant

Related

Initialize the arrays inside an array of char* using a loop

I need to create an array of strings, each representing a card of the Spanish deck:
#define __USE_MINGW_ANSI_STDIO 1
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(void)
{
char *type[4]= {"copas", "basto", "espada", "oro"};
char *number[10]= {"Uno", "Dos", "Tres", "Cuatro", "Cinco", "Seis", "Siete", "Diez", "Once", "Doce"};
char *deck[40];
int deckIndex= 0;
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 10; j++) {
char card[100] = "";
strcat(card, number[j]);
strcat(card, " de ");
strcat(card, type[i]);
strcat(card, "\n");
deck[deckIndex]= card;
deckIndex++;
}
}
for (int i = 0; i < 40; i++)
{
printf("%s\n", deck[i]);
}
return 0;
}
However, all entries of deck[] point to the same string. As a result, "Doce de oro" is printed 40 times. I don't understand why this happens, but I've theorized it's because card[] is being reinitialized in the same memory direction, and overrides what was already written there in the previous iteration. If I'm right, I would have to declare every array separately, but I have no idea how to do that without writing 40 different arrays.
Tldr:
¿Why do all entries of deck[] point to the same location?
¿How do I fix it?
(Btw suggestions for a better title are appreciated)
In C, memory on the stack is allocated in terms of Scopes. So yes, your theory is right. You are rewriting on the same location.
To fix your program, there are two possible solutions I can think of.
You can use Multidimensional Arrays.
Or you can allocate memory in heap using malloc (but make sure to free it once you are done with it)
As pointed out in the comments, in the deck[deckIndex]= card; line, you are assigning the same pointer1 to each of your deck elements – and, worse, a pointer to a variable (the card array) that is no longer valid when the initial nested for loop goes out of scope.
To fix this, you can make copies of the card string, using the strdup function, and assign the addresses of those copies to the deck elements. Further, as also mentioned in the comments, you can simplify the construction of the card string using a single call to sprintf, rather than using multiple strcat calls.
Here's how you might do that:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(void)
{
char* type[4] = { "copas", "basto", "espada", "oro" };
char* number[10] = { "Uno", "Dos", "Tres", "Cuatro", "Cinco", "Seis", "Siete", "Diez", "Once", "Doce" };
char* deck[40];
int deckIndex = 0;
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 10; j++) {
char card[100] = "";
sprintf(card, "%s de %s", number[j], type[i]);
deck[deckIndex] = strdup(card);
deckIndex++;
}
}
for (int i = 0; i < 40; i++) {
printf("%s\n", deck[i]);
}
// When you're done, be sure to free the allocated memory:
for (int i = 0; i < 40; i++) {
free(deck[i]);
}
return 0;
}
If your compiler does not support the strdup function (most do, and it is part of the ISO C Standard from C23), writing your own is very simple:
char* strdup(const char *src)
{
char* result = malloc(strlen(src) + 1); // Add one to make room for the nul terminator
if (result) strcpy(result, src);
return result;
}
1 Well, formally, a new card array is born on each iteration of the inner for loop, but it would be a very inefficient compiler that chose to do that, rather than simply re-using the same memory – which is clearly what is happening in your case.

If I allocate only 4 bytes for single int, how then pointer can store second int?

As I found on one site following code:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int *p1 = malloc(4*sizeof(int)); // allocates enough for an array of 4 int
int *p2 = malloc(sizeof(int[4])); // same, naming the type directly
int *p3 = malloc(4*sizeof *p3); // same, without repeating the type name
if(p1) {
for(int n=0; n<4; ++n) // populate the array
p1[n] = n*n;
for(int n=0; n<4; ++n) // print it back out
printf("p1[%d] == %d\n", n, p1[n]);
}
free(p1);
free(p2);
free(p3);
}
Output:
p1[0] == 0
p1[1] == 1
p1[2] == 4
p1[3] == 9
Now following that code above I did this:
#include <stdio.h>
#include <stdlib.h>
#include "Header.h"
int main()
{
int *ip = (int*)malloc(1 * sizeof(int));
ip[0] = 2;
ip[1] = 9;
printf("%d %d",ip[0],ip[1]);
return 0;
}
Output: 2 9
So how then my pointer *ip can store more than one int when I only allocated bytes for single int?
You're writing into memory which is outside of the bounds of the array. Sadly, there is no compiler check for this, but it sometimes works.
Note: This is undefined behavior and should not be used under any circumstances.
Remember, ip[1] is equivalent to *(ip + 1), which is syntactically valid for any pointer, even if ip is pointing to single int and not an array of ints.
Dereferencing or writing to ip[1] is undefined behavior. Try adding free(ip) before the return 0 in your second program and observe the behavior then.

C - What's wrong with my code (malloc, char*)

I just want you to ask what did I do wrong with this code.
I wrote a function that take a char* in parameter, I want to modify it directly without returning smthg, and reverse the string.
#include <iostream>
void reverseString(char *p_string){
int length = strlen(p_string);
int r_it = length - 1;
char* tmp = (char*)malloc(length);
int last_it = 0;
for (int i = 0; i != length; i++){
tmp[i] = p_string[r_it];
r_it--;
last_it++;
}
tmp[last_it] = '\0';
strcpy_s(p_string, length + 1, tmp);
//free(tmp);
}
int main(){
char str[] = "StackOverflow";
reverseString(str);
std::cout << str << std::endl;
system("pause");
}
I'm used to C++ and don't often use C functions like malloc/free/strcpy...
Here, my problem is, when I alloc memory for my temporary char, I called mallec(length) for length = 13 in this case, char = 1 bytes so it should be allocate memory for 13 char is that right?
Problem is allocate more space than need so i need to use '\0' before my strcpy_s if not it breaks.
Did I do a mistake somewhere?
Also, when i call free(tmp), it breaks too and say heap corruption, but I didn't free the memory before that.
Thanks for helping !
I took your original code and added a simple '+1' to the size of the malloc and got a passing result.
Not sure if your exercise is related specifically to the use of malloc, but have you considered doing the reversal directly inside the original string?
For example:
void reverseString(char *p_string){
char* p_end = p_string+strlen(p_string)-1;
char t;
while (p_end > p_string)
{
t = *p_end;
*p_end-- = *p_string;
*p_string++ = t;
}
}
int main(){
char str[] = "StackOverflow";
reverseString(str);
std::cout << str << std::endl;
system("pause");
}
If you are required to use malloc, then you need to ensure that you allocate enough space for string which includes the '\0'
You must use
int length = strlen(p_string);
int r_it = length - 1;
char* tmp = (char*)malloc(length+1);
Since strlen doesn't count the \0 character. So this will fail if you don't use length+1:
tmp[last_it] = '\0';
The length of a C string is determined by the terminating
null-character: A C string is as long as the number of characters
between the beginning of the string and the terminating null character
(without including the terminating null character itself).
http://www.cplusplus.com/reference/cstring/strlen/
Btw. C99 support semi dynamic arrays. So could you try this:
char tmp[length+1];
Source:
http://en.wikipedia.org/wiki/Variable-length_array
float read_and_process(int n)
{
float vals[n];
for (int i = 0; i < n; i++)
vals[i] = read_val();
return process(vals, n);
}
Check the below C code:
The memory allocated to tmp should be length+1 as done below and also there are many unnecessary variables which can be avoided.
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
void reverseString(char *p_string){
int i;
int length = strlen(p_string);
int r_it = length - 1;
char* tmp = (char*)malloc(length+1);
for (i = 0; i != length; i++){
tmp[i] = p_string[r_it--];
}
tmp[i] = '\0';
strcpy(p_string, tmp);
return;
}
int main(){
char str[] = "StackOverflow";
reverseString(str);
printf("%s",str);
return 0;
}
There is nothing fundamentally wrong with your approach, just some of the details. Since I am not sure how you found out that the sizeof(tmp) is 32, I modified your code to the one below which includes a few printfs and some minor changes:
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
void reverseString(char *p_string)
{
size_t length = strlen(p_string);
size_t r_it = length - 1;
char* tmp = (char*)malloc(length+1);
int last_it = 0;
size_t i=0;
printf("strlen(p_string) = %d\n", strlen(p_string));
printf("Before: strlen(tmp) = %d\n", strlen(tmp));
for (i = 0; i != length; i++) {
tmp[i] = p_string[r_it];
r_it--;
last_it++;
}
tmp[last_it] = '\0';
printf("After: strlen(tmp) = %d\n", strlen(tmp));
strcpy(p_string, tmp);
free(tmp);
}
int main()
{
char str[] = "StackOverflow";
reverseString(str);
printf("%s\n", str);
return 0;
}
First, I have removed all C++ specific code - you can now compile this with gcc. Running this code yields this output:
sizeof(p_string) = 13
Before: strlen(tmp) = 0
After: strlen(tmp) = 13
wolfrevOkcatS
This is to be expected - strlen basically counts bytes until it hits the \0 character and so the first time we print the size using strlen, it returns 0 since we just allocated the memory. As another poster suggested, we have to allocate 1 extra byte to store the \0 in our new string.
Once the reverse is complete, 13 bytes would have been copied over to this memory and the second strlen returns the expected answer.

Populating a buffer from the return of a method fails... ANSI C

The code I have is quite simple in one method I have this:
// This line has an Intellisense Error: Initialization with {...} expected for aggregate object
char str[] = GetBuffer(); // x 64 will give us 512 (sector sized buffer) ;
The GetBuffer metod is this:
char * GetBuffer(void)
{
int idx = 0;
int offset = 0;
char *buffer[512];
for(idx =0; idx < 64; idx ++)
{
// This line has an Itellisense Error: "Expected Expression"
buffer[offset + idx] = {"E","R","A","S","E","D"," ", " "};
offset += 8;
}
return *buffer;
}
Any ideas what's wrong with this?
All I am trying to do - is populate a buffer with 512 bytes which contain the following string repeated: "ERASED " This is ANSI C (not C++) and it has been so long since I coded in ANSI C - please help and be kind!
Using Visual Studio 2012
EDIT 1
Ok lots of things have been fixed thanks to you guys - but no full answer yet.
The str buffer holds 528 characters and not 512 and contains a lot of ERASED as expected but ends with
ýýýý««««««««îþîþ
Any ideas with this? And Oh boy I have a great deal of pure C reading to do - I have forgotten way too much!
You can't initialize an array with the return value from a function.
You could use a pointer instead of an array:
char *str = GetBuffer();
Or you could use strcpy() or a relative — but there are buffer overflow risks:
char str[512];
strcpy(str, GetBuffer());
Your GetBuffer() function also has a lot of problems.
char *GetBuffer(void)
{
int idx = 0;
int offset = 0;
char *buffer[512];
This should probably be char buffer[512];, but...
for(idx =0; idx < 64; idx ++)
{
// This line has an Itellisense Error: "Expected Expression"
buffer[offset + idx] = {"E","R","A","S","E","D"," ", " "};
You can't set arrays like this. And you needed double quotes because of the char *buffer[512] problem.
offset += 8;
}
return *buffer;
}
And you should not return a local variable — it is destroyed when the function returns so it can't be used afterwards.
You might write:
char *GetBuffer(void)
{
char *buffer = malloc(257);
if (buffer != 0)
{
int idx;
for (idx = 0; idx < 256; idx += 8)
strcpy(buffer+idx, "ERASED ");
}
return buffer;
}
There's a small layer of obfuscation going on with the hard-coded lengths and limits; they're correct, but the interconnections between the sizes are not obvious — and ideally, they should be:
strlen("ERASED ") == 8
256 = 32 * strlen("ERASED ")
257 = 32 * strlen("ERASED ") + 1 (the one is for the terminal null)
And then the calling code might be:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
char *str = GetBuffer();
if (str != 0)
{
printf("<<%s>>\n", str);
free(str);
}
return(0);
}
there is problem with your buffer creation. you'd malloc such that it's not reclaimed by the function invoke routine. Second, you can't do assignment like the line you encountered a Itellisense error.
You can use this:
#include "stdlib.h"
char * GetBuffer(void)
{
int i = 0, idx = 0;
const char * cstr_init = "ERASED ";
char *buffer = (char*)malloc(512);
for (idx = 0; idx < 512; idx+=8) {
for (i = 0; i < 8; i++) {
buffer[idx+i] = cstr_init[i];
}
}
return buffer;
}
There are several things wrong here.
In C, a character array can be initialized with an initializer list or a string literal. You cannot use the return value from a function to initialize the array. So
char str[] = GetBuffer();
will not work.
Also, char* buffer [512] is an array of 512 pointers to char, i.e., an array of 512 strings. buffer [offset + idx] would be one pointer to char. It can hold only one string, but you are trying to assign eight strings to it: "E", "R", etc. If you mean those to be chars and not strings, use single quotes: 'E', etc. However, even that won't work unless you allocate memory to the pointer so that it can hold the string.
As written, the array of pointers is allocated on the stack, so it goes out of scope when the function terminates. return *buffer would return the first string in the array of strings, but that's a local variable, so you're returning the dereferenced value of a pointer that is no longer in scope.
I think a simpler way to accomplish your goal is this:
char str [512] = {'\0'};
for (int i = 0; i < 511; i += 7)
strcat (str + i, "ERASED ");
It's not very general, but it does what you want.
Edited to reflect Jonathan Leffler's comment that strcat (str, "ERASED "), which is what I originally had, is inefficient.

Pointers aren't being transferred to allocated memory

I have a school assignment, and everything works just fine except for one part, which I cannot figure out. The following is the snippet of code where my issue is occurring:
//something goes wrong here, wont copy over
puts("bravo");
//add to end of the list
int size_list = sizeof(environ);
//char** tmp_ptr = ( char** )calloc( size_list + 1, ( size_list + 1 ) * sizeof( char* ) );
char** tmp_ptr = ( char** ) malloc ( ( size_list + 1 ) * sizeof( char* ) );
int k;
for ( k = 0; k < size_list; ++k )
{
tmp_ptr[k] = environ[k];
//memcpy(tmp_ptr[k],environ[k],sizeof(environ[k]));
}
//char** tmp_ptr= (char**)realloc(*environ, size_list+2);
environ = tmp_ptr;
environ[size_list] = (char*)malloc(len_string+1);
strcpy(environ[size_list],full_string);
return 1;
You can ignore the "bravo", it was for me to locate where the problem was occurring. I am trying to get environ to have the new list of variables, but when I set it equal to tmp_ptr, it is empty. I'm pretty sure that the values are getting copied over, but I do not know what is wrong.
Does the data in tmp_ptr get deleted when the function ends? Is that a possible explanation? How can I properly allocate and copy over the memory. I tried using realloc, but that gave me invalid pointer errors, so I am relying on calloc or malloc. Thanks in advance.
environ's length isn't sizeof(environ), since environ is a char ** (and thus sizeof(environ) is 4 or 8 depending on your platform). What you're doing is effectively wiping out most of environ because you only copy the first few entries.
To find out how many entries are in environ, do
int cnt = 0;
char **envp = environ;
while(*envp++) cnt++;
I should also note a problem with the approach: since environ is a NULL-terminated string array (not to be confused with 'null-terminated string'), you have to replace the NULL at the end with your own entry, then add a new NULL after your new entry. Currently, your code (if it calculated the size correctly) would have added your entry after the NULL, and would have not been visible to the program.
Side note: messing with environ this way is definitely not recommended. Use getenv/setenv instead; if you need to mass-set the environment, try using execve instead. (So, in your case, you can simply setenv("varname", "varvalue", 1) to add varname=varvalue to the environment (and replace an existing mapping to varname if it is already set).
environ is a pointer, meaning that sizeof(environ) is not the number of items in that array. Rather, it's the size of the pointer, probably four or eight in your case.
If environ is the same sort of structure as argv (an array of character pointers where the last one is a null pointer), you will need to determine its size by walking through the array until you find NULL.
Something like (untested, but the idea is sound):
char **envPtr = environ;
int size_list = 0; // probably should be size_t
while (*envPtr != NULL) {
envPtr++;
size_list++;
}
You can see the effect in this complete program (with a few changes to get around some other bugs):
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define full_string "xyzzy=plugh"
#define len_string strlen(full_string)
int main (int argc, char **argv) {
int k, size_list = sizeof(environ);
char **tmp_ptr = malloc ((size_list + 1) * sizeof ( char*));
for (k = 0; k < size_list; ++k) printf ("[%s]\n", environ[k]);
for (k = 0; k < size_list; ++k) tmp_ptr[k] = environ[k];
tmp_ptr[size_list] = NULL;
environ = tmp_ptr;
environ[size_list] = malloc (len_string + 1);
strcpy(environ[size_list],full_string);
printf ("=====\n");
for (k = 0; k <= size_list; ++k) printf ("[%s]\n", environ[k]);
return 0;
}
This outputs only four of my environment variables because I have four-byte pointers:
[ORBIT_SOCKETDIR=/tmp/orbit-pax]
[SSH_AGENT_PID=1978]
[GPG_AGENT_INFO=/tmp/seahorse-tNDhG9/S.gpg-agent:2005:1]
[TERM=xterm]
=====
[ORBIT_SOCKETDIR=/tmp/orbit-pax]
[SSH_AGENT_PID=1978]
[GPG_AGENT_INFO=/tmp/seahorse-tNDhG9/S.gpg-agent:2005:1]
[TERM=xterm]
[xyzzy=plugh]
Changing the code to correctly determine the size of the environment:
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define full_string "xyzzy=plugh"
#define len_string strlen(full_string)
int main(int argc, char **argv) {
int k, size_list = 0;
char **envPtr = environ;
while (*envPtr != NULL) {
envPtr++;
size_list++;
}
char **tmp_ptr = malloc ((size_list + 1) * sizeof (char*));
for (k = 0; k < size_list; ++k) printf ("[%s]\n", environ[k]);
for (k = 0; k < size_list; ++k) tmp_ptr[k] = environ[k];
tmp_ptr[size_list] = NULL;
environ = tmp_ptr;
environ[size_list] = malloc (len_string + 1);
strcpy(environ[size_list],full_string);
printf ("=====\n");
for (k = 0; k <= size_list; ++k) printf ("[%s]\n", environ[k]);
return 0;
}
gives me the lot:
[ORBIT_SOCKETDIR=/tmp/orbit-pax]
[SSH_AGENT_PID=1978]
[GPG_AGENT_INFO=/tmp/seahorse-tNDhG9/S.gpg-agent:2005:1]
[TERM=xterm]
[SHELL=/bin/bash]
[GTK_RC_FILES=/etc/gtk/gtkrc:/home/pax/.gtkrc-1.2-gnome2]
[WINDOWID=62914564]
[GNOME_KEYRING_CONTROL=/tmp/keyring-RADe9n]
[GTK_MODULES=canberra-gtk-module]
[USER=pax]
:
[XAUTHORITY=/var/run/gdm3/auth-for-pax-AO1dYc/database]
[_=./testprog]
=====
[ORBIT_SOCKETDIR=/tmp/orbit-pax]
[SSH_AGENT_PID=1978]
[GPG_AGENT_INFO=/tmp/seahorse-tNDhG9/S.gpg-agent:2005:1]
[TERM=xterm]
[SHELL=/bin/bash]
[GTK_RC_FILES=/etc/gtk/gtkrc:/home/pax/.gtkrc-1.2-gnome2]
[WINDOWID=62914564]
[GNOME_KEYRING_CONTROL=/tmp/keyring-RADe9n]
[GTK_MODULES=canberra-gtk-module]
[USER=pax]
:
[XAUTHORITY=/var/run/gdm3/auth-for-pax-AO1dYc/database]
[_=./testprog]
[xyzzy=plugh]
For what it's worth, your code had the following problems:
The incorrect calculation of the environment size, obviously.
Casting the return value from malloc - this is ill-advised in C since it can cause code problems to be hidden from you.
Not setting the final element of the new list to NULL.
Not checking for out-of-memory conditions.
All these except the last are fixed in my final code sample above.

Resources