I call a function that swaps shifts a massage in memory:
for (int i = now->size - 1; i >= 0; i--)
{
void *address2 = prev->start_address + i;
void *address1 = now->start_adress + i;
address1 = address2;
address2 = '\0';
}
So basically I have two addresses one pointing to the first start location the other to the second start location that the content have to be pasted.
The problem is that the only solution that I find is to add int ( this is i value ) and prev->start_adress( that is void*) as I have shown. I want to do it correctly, i cant change the void pointer to int. Is there any other possibilities.
My errors:
error: invalid conversion from ‘void*’ to ‘char*’ [-fpermissive]
warning: pointer of type ‘void *’ used in arithmetic [-Wpointer-arit]
214 | void *address2 = prev->start + i;
Warning: pointer of type ‘void *’ used in arithmetic [-Wpointer-arit]
215 | void *address1 = now->start + i;
Supporting information:
I have a linked list(full of segementdescriptors) and a "memory" that is a simple array[].Similar how malloc works.
typedef struct segmentdescriptor
{
Byte allocated;
void *start;
size_t size;
struct segmentdescriptor *next;
} Node;
the start pointers point to the begining of the allocated space in the array[].
Update:
The simplest way is to use typecast to do arithmethics on void pointers if you know their size like :
char *address2 = (char *)prev->start + i;
If you dont know the type, it is impossible because for example:
char *pointer points to one byte of memory and if you write pointer++ goes
to next byte. int *pointer is lets say points four byes. if you write pointer ++ goes to the four bytes after the four bytes.
There are also good answers below.
Thanks for all answers.
I do not really understand what shifts a massage in memory is, but if you just want to copy one memory location to another you can
Use memcpy or memmove (if memory locations overlap)
memcpy(now->start_adress, prev->start_address, now->size);
Write your own function to copy the memory
void *mymemcpy(void *dest, const void *src, size_t size)
{
unsigned char *cdest = (unsigned char *)dest; //cast for C++ compiler
const unsigned char *csrc = (unsigned char *)src;
while(size--) *cdest++ = *csrc;
return dest;
}
void *mymemcpy(void *dest, const void *src, size_t size)
{
unsigned char *cdest = (unsigned char *)dest; //cast for C++ compiler
const unsigned char *csrc = (unsigned char *)src;
for(size_t index = 0; index < size; index++) cdest[index] = csrc[index];
return dest;
}
void *mymemcpy(void *dest, const void *src, size_t size)
{
unsigned char *cdest = (unsigned char *)dest; //cast for C++ compiler
const unsigned char *csrc = (unsigned char *)src;
for(size_t index = 0; index < size; index++) *(cdest + index) = *(csrc + index);
return dest;
}
C standard does not allow any pointer arithmetic on void pointers. gcc has an extension which treats the void * as pointer to char allowing the arithmetics, but not allowing dereferencing.+
The type void is always an incomplete type. That is the size of an object of the type void is unknown. So you may not use the pointer arithmetic with a pointer of the type void * though some compilers have their own language extensions that allow the pointer arithmetic with the type void * the same way as with pointers of the type char *.
So these statements
void *address2 = prev->start + i;
void *address1 = now->start + i;
are incorrect according to the C Standard because the pointer start has the type void *.
Also it seems these statements
address1 = address2;
address2 = '\0';
are doing not what you thing.
For starters this statement
address1 = address2;
assigns one pointer to another pointer though it looks like actually you want to assign a value pointed to by one pointer to the memory pointed to by another pointer.
And this statement is equivalent to
address2 = NULL;
That is it does not set the pointed memory with the terminating zero character '\0'.
If as you are saying these pointers deal with a message (a string) then you need to cast the pointers to the type char *.
As for this your phrase
I call a function that swaps shifts a massage in memory:
then it is totally unclear what you are trying to do using the for loop because the loop in any case does not make a sense.
If you need to copy one character array into another character array then use the standard C function memcpy or memmove depending on what and how you are trying to copy arrays.
Regarding these warnings:
warning: pointer of type ‘void *’ used in arithmetic [-Wpointer-arit]
214 | void *address2 = prev->start + i;
Warning: pointer of type ‘void *’ used in arithmetic [-Wpointer-arit]
215 | void *address1 = now->start + i;
On any modern computer the value of a pointer is a memory address (typically a virtual memory address). When you add one to a pointer, it's value (aka the memory address) is incremented by the sizeof the pointed to type.
Example:
TYPE* p = SOME_ADDRESS; // The value of p is now SOME_ADDRESS
p = p + 1; // The value of p is now SOME_ADDRESS + sizeof(TYPE);
p = SOME_ADDRESS; // The value of p is now SOME_ADDRESS
p = p + i; // The value of p is now SOME_ADDRESS + i * sizeof(TYPE);
In other words: In order to add an integer value to a pointer, we need to know the size of the element that the pointer points to.
And that is your problem. You have void-pointers so you would need to know the "sizeof void" in order to update the pointer. But the sizeof void can be anything and isn't defined by the standard. Apparently, your compiler has a non-standard extension that allows sizeof(void). Consequently, your compiler only gives you a warning.
Regarding this error:
error: invalid conversion from ‘void*’ to ‘char*’
It comes from some code post you didn't post. Further it shows that you use a c++ compiler instead of a c compiler.
Finally: Even if the pointer arithmetic was valid, your code do nothing.
for (int i = now->size - 1; i >= 0; i--)
{
void *address2 = prev->start_address + i;
void *address1 = now->start_adress + i;
address1 = address2; // This only change the value of the pointer
// It doesn't change the memory that the
// pointer points to
address2 = '\0';
}
It's not fully clear what you are trying to do but perhaps this is what you are looking for:
for (int i = now->size - 1; i >= 0; i--)
{
char *address2 = (char*)prev->start_address + i;
char *address1 = (char*)now->start_adress + i;
*address1 = *address2;
*address2 = '\0';
}
Related
I was just creating a testing function in which i have to pass boolean in void * so that i can parse it in other function and use it.
but i am stuck and not able to know that how should i memcpy the boolean in void *.
but when i am parsing it in another fucntion i am always getting the value true.
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
typedef struct {
int a;
uint8_t var_data[];
} s;
void parse(s * dummy)
{
void *var_data = dummy->var_data;
uint8_t *len;
char type[128];
bool *leaf;
for(int i = 0; i < dummy->a; i++)
{
len = (uint8_t *)var_data;
var_data += 1;
memcpy(type, var_data, *len);
type[*len] = '\0';
var_data += *len;
leaf = (bool *)var_data;
var_data += 1;
printf("%s\n", type);
printf("leaf: %s\n\n", leaf ? "true" : "false");
}
}
int main()
{
// Write C code here
char val[] = "dummy value";
uint8_t len = strlen(val);
bool v = false;
int b = 2;
int sz = sizeof(s) + b * (sizeof(bool) + len + 1);
s * dummy = (s *) malloc(sz);
dummy->a = b;
void *var = dummy->var_data;
for(int i = 0; i < dummy->a; i++){
memcpy(var, &len, 1);
var += 1;
memcpy(var, val, len);
var += len;
memcpy(var, &v, sizeof(bool));
var += sizeof(bool);
}
parse(dummy);
return 0;
}
can body help me with this problem.
var_data is uninitialized. You should allocate var_data with malloc, and copy the data in leaf into it:
void *var_data = malloc(sizeof(bool));
bool leaf = false;
memcpy(var_data, &leaf, sizeof(bool));
And you can cast it to bool * like this:
bool *leaf;
leaf = (bool *) var_data;
In addition, you increment var_data pointer. So var_data points to a different memory location now.
You didn't dereference leaf in this line:
printf("leaf: %s\n\n", leaf ? "true" : "false");
Since leaf is a non-zero pointer, it will always evaluate to true in C. You want to print *leaf instead:
printf("leaf: %s\n\n", *leaf ? "true" : "false");
Some other misc remarks:
void* arithmetic (i.e. var_data += 1) is illegal in C, although gcc will not complain. Use a char* because this is the type that's supposed to be used for serialization.
As mentioned in other answers, using pointers like you are doing right now can lead to subtle errors. If your pointer is pointing to an address and you want to dereference it (read the value stored there), it's better to do this sooner than risk this location being changed by some other code in the meantime.
So, just copy the data from the char* array into the target struct (or a primitive like uint8_t) and then advance the pointer.
The only way you are technically allowed to cast pointers in C is to cast them from a specific pointer (like something*) to a char*, in order to inspect their contents. You can also implicitly cast from and to void*, but only if you are not aliasing the pointer (trying to modify the underlying type). Any casting in the other direction is a violation of strict aliasing, so you should try to use memcpy instead. It may look uglier, but compiler will optimize it anyway (see for yourself) and you'll be safe(r) from the horrors of aliasing.
One nice habit to have it to try to utilize const-correctness wherever you can, it helps the compiler warn you if you're doing something wrong. If your function is parsing the array, the parameter should be const char*.
Finally, if your goal is to serialize and deserialize structs, perhaps you should look into protocol buffers or some similar serialization framework. It is fast, efficient, portable and, best of all, already written.
So, something like:
typedef struct {
int len;
char * var_data;
} example;
// note the const keyword - this means this function is
// not going to change the struct, only read it
void parse(const example * dummy)
{
// again, pointer to const char
const char * var_data = dummy->var_data;
// move all variables to the innermost scope
for (int i = 0; i < dummy->len; i++)
{
uint8_t len = 0;
memcpy(&len, var_data, sizeof(len));
var_data++;
...
}
}
I am implementing a generic sort in C according to an assignment. In this assignment I need to get and sort (in bubble sort) an array of type void. The function signature is:
void bubbleSort(void* arrayToSort,int lenArray,size_t sizeElements, int (*compare)(const void*,const void*))
I got a peroblem: I find this error "expression must be a pointer to a complete object type" almost in everything I am doing. I searched, but all the advises about this error are to cast it first to the real type. I can't cast it, because the function is ment to be generic, and all I know is the size of the array's type. Can someone help me?
This is the code:
void bubbleSort(void* arrayToSort,int lenArray,size_t sizeElements, int (*compare)(const void*, const void*)) {
bool didExcanged = false;
for (int i = 0; i < lenArray && (!didExcanged); i++)
{
void* pivot = arrayToSort + i * sizeElements;//that error on "arrayToSort"
didExcanged = false;
for (int j = i; j < lenArray; j++)
{
void* toCompare = arrayToSort + j * sizeElements;//that error on "arrayToSort"
if (compare(pivot,toCompare)>0) {
swap(pivot, toCompare);
didExcanged = true;
}
}
}
}
and the swap function:
void swap(void* a, void* b) {
void *temp=*a;//that error on "*a"
*a = *b;//that error on "*a" and "*b"
*b = *temp;//that error on "*b" and "*temp"
}
You can't de-reference a void*, because the void type doesn't have a size - it's an incomplete type hence the warning "must be a complete object type".
You can't write a generic swap function based on void* without knowing the size of the data objects, so you must pass that information along to swap.
void* is a weird one in C. It is technically a pointer, but it also differs from other pointer types in that it cannot be de-referenced. Nor can you do pointer arithmetic on it. You can store and retrieve any pointer type in it, but you cannot do anything with it other than store it or typecast it into another type. In your situation, the only viable approach seems to be the one that a memcpy implementation would use - typecast it to an unsigned char * which is guaranteed to be de-referenceable for any type.
Example:
void my_memcpy(void* a, void* b, size_t size)
{
unsigned char *p = (unsigned char*)a;
unsigned char *q = (unsigned char*)b;
while(size--)
{
*p++ = *q++;
}
}
A swap can be implemented using memcpy as:
void swap(void* a, void* b, size_t size)
{
unsigned char temp[size];
memcpy(temp, a, size);
memcpy(a, b, size);
memcpy(b, temp, size);
}
I'm not great with pointers. I know enough to get an array of pointers to char to work, as in the first example below. But I don't want to pass an entire array of pointers, because it takes up too much room on the stack. What I would like to do is pass a single pointer to the memory allocated for the array of pointers. I have no idea how to do this.
This program works:
#include "pch.h"
#include "$StdHdr.h"
#include "TmpTstPtr1.h"
#define SRC_LIN_SIZ 150
int main(int ArgCnt, char * ArgVal[])
{
char InpFilPth[MAX_PATH + 1];
FILE * InpFilPtr;
char ** SrcArr;
unsigned Sub1;
unsigned SrcArrCnt = 0;
strncpy_s(InpFilPth, "TmpTstPtr1.cpp", strlen("TmpTstPtr1.cpp"));
fopen_s(&InpFilPtr, InpFilPth, "r");
SrcArr = (char **)malloc(999999 * sizeof(char *));
LodSrcArr(InpFilPtr, SrcArr, &SrcArrCnt);
for (Sub1 = 0; Sub1 < SrcArrCnt; Sub1++) {
printf("SrcArr[%d] = %s\n", Sub1, SrcArr[Sub1]);
}
fclose(InpFilPtr);
return 0;
}
void LodSrcArr(FILE * InpFilPtr, char ** SrcArr, unsigned * SrcArrCnt)
{
char SrcLin[SRC_LIN_SIZ + 1];
char * GetStrPtr;
GetStrPtr = GetStr(SrcLin, SRC_LIN_SIZ, InpFilPtr);
while (GetStrPtr != NULL) {
SrcArr[*SrcArrCnt] = (char *)malloc(SRC_LIN_SIZ + 1);
// CpySiz(SrcArr[*SrcArrCnt], strlen(SrcLin) + 1, SrcLin);
errno = strncpy_s(SrcArr[*SrcArrCnt], SRC_LIN_SIZ + 1, SrcLin, strlen(SrcLin));
(*SrcArrCnt)++;
GetStrPtr = GetStr(SrcLin, SRC_LIN_SIZ, InpFilPtr);
}
}
char * GetStr(char * Str, const int MaxChr, FILE * InpFilPtr)
{
char * InpRtnVal = NULL;
unsigned Sub1;
// Get string from input file. Find the end of the string if something entered.
InpRtnVal = fgets(Str, MaxChr + 1, InpFilPtr);
if (InpRtnVal != NULL) {
Sub1 = 0;
while (Str[Sub1] != '\n' && Str[Sub1] != '\0') {
Sub1++;
}
// Replace newline with null.
if (Str[Sub1] == '\n') {
Str[Sub1] = '\0';
}
}
return InpRtnVal;
The following program doesn't even come close:
#include "pch.h"
#include "$StdHdr.h"
#include "TmpTstPtr2.h"
#define SRC_LIN_SIZ 150
int main(int ArgCnt, char * ArgVal[])
{
char InpFilPth[MAX_PATH + 1];
FILE * InpFilPtr;
char ** SrcArr;
unsigned Sub1;
unsigned SrcArrCnt = 0;
char *** SrcArrPtr = NULL;
strncpy_s(InpFilPth, "TmpTstPtr2.cpp", strlen("TmpTstPtr2.cpp"));
fopen_s(&InpFilPtr, InpFilPth, "r");
SrcArr = (char **)malloc(999999 * sizeof(char *));
SrcArrPtr = &SrcArr;
LodSrcArr(InpFilPtr, SrcArrPtr, &SrcArrCnt);
SrcArrPtr = &SrcArr;
for (Sub1 = 0; Sub1 < SrcArrCnt; Sub1++) {
// printf("SrcArr[%d] = %s\n", Sub1, SrcArr[Sub1]); // got "Exception thrown: read access violation. it was 0xCDCDCDCD."
printf("SrcArr[%d] = %s\n", Sub1, **SrcArrPtr); // get 75 lines of garbage
(**SrcArrPtr) += sizeof(char *);
}
fclose(InpFilPtr);
return 0;
}
void LodSrcArr(FILE * InpFilPtr, char *** SrcArrPtr, unsigned * SrcArrCnt)
{
char SrcLin[SRC_LIN_SIZ + 1];
char * GetStrPtr;
GetStrPtr = GetStr(SrcLin, SRC_LIN_SIZ, InpFilPtr);
// while (GetStrPtr != NULL and *SrcArrCnt == 0) {
while (GetStrPtr != NULL) {
**SrcArrPtr = (char *)malloc(SRC_LIN_SIZ + 1);
// CpySiz(SrcArr[*SrcArrCnt], strlen(SrcLin) + 1, SrcLin);
errno = strncpy_s(**SrcArrPtr, SRC_LIN_SIZ + 1, SrcLin, strlen(SrcLin));
(**SrcArrPtr) += sizeof(char *);
(*SrcArrCnt)++;
GetStrPtr = GetStr(SrcLin, SRC_LIN_SIZ, InpFilPtr);
}
}
char * GetStr(char * Str, const int MaxChr, FILE * InpFilPtr)
{
char * InpRtnVal = NULL;
unsigned Sub1;
// Get string from input file. Find the end of the string if something entered.
InpRtnVal = fgets(Str, MaxChr + 1, InpFilPtr);
if (InpRtnVal != NULL) {
Sub1 = 0;
while (Str[Sub1] != '\n' && Str[Sub1] != '\0') {
Sub1++;
}
// Replace newline with null.
if (Str[Sub1] == '\n') {
Str[Sub1] = '\0';
}
}
return InpRtnVal;
}
As the comments say, when I try to access SrcArr via a subscript, I get a run-time error. When I try to access via the pointer, I get garbage. The problem may be where I say SrcArrPtr = &SrcArr;. I don't know if it's significant, but the garbage printed is 4 characters shorter with each subsequent line. As if it's actually printing the array of pointers itself, rather than the strings they point to. I dunno.
The reason I coded it as above is in order to get the program to compile. I've never tried to use 3 pointers before. Is what I'm trying to do even possible? If so, can someone show me how? An explanation of how it works would be nice, but not necessary. (I'm using Visual Studio 2017, though I don't think it matters.)
TIA.
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
void foo(char* bar[10]) { // a real array
for (int i = 0; i < 10; ++i) {
bar[i] = calloc(2, 1);
bar[i][0] = '0' + i;
}
}
void xox(char **qux) { // pointer to some char-pointers on the heap
for (int i = 0; i < 10; ++i) {
qux[i] = calloc(2, 1);
qux[i][0] = '0' + i;
}
}
int main(void)
{
char* bar[10]; // a "real" array
foo(bar);
for (size_t i = 0; i < 10; ++i)
puts(bar[i]);
putchar('\n');
// cleanup:
for (size_t i = 0; i < 10; ++i)
free(bar[i]);
// plan b:
char **qux = calloc(10, sizeof(*qux));
xox(qux);
for (size_t i = 0; i < 10; ++i)
puts(qux[i]);
putchar('\n');
// cleanup:
for (size_t i = 0; i < 10; ++i)
free(qux[i]);
free(qux);
}
What I would like to do is pass a single pointer to the memory
allocated for the array of pointers.
Suppose you have some integers on the heap, like this:
int *integers = (int*)malloc(4 * sizeof(int));
And now suppose you have some pointers, also on the heap:
int **pointers = (int**)malloc(4*sizeof(int*));
Now let's assign the pointers to the addresses of the integers:
pointers[0] = &integers[0];
pointers[1] = &integers[1];
pointers[2] = &integers[2];
pointers[3] = &integers[3];
In this example, pointers is a pointer to an array of pointers (on the heap) pointing to some integers (also on the heap). You can freely pass pointers around and use it in another function.
Or, if you wanted the array of pointer to be on the stack:
int* pointers[4];
pointers[0] = &integers[0];
pointers[1] = &integers[1];
pointers[2] = &integers[2];
pointers[3] = &integers[3];
int **ppointer = pointers;
Now ppointer is also a pointer pointing to an array of pointers that point to some integers on the heap. Just notice that this time, those pointers are on the stack, not on the heap. So if you return from this function, they're out of scope and you may not access them anymore.
You're operating under a misconception. Neither C nor C++ pass a copy of an array to a function, nor can they return an array from a function.
Except when it is the operand of the sizeof or unary & operators, or is a string literal used to initialize a character array in a declaration, an expression of type "N-element array of T" will be converted ("decay") to an expression of type "pointer to T", and the value of the expression is the address of the first element.
Thus, if you declare an array like
char *ptrs[N];
and pass it to a function as
foo( ptrs );
the expression ptrs is implicitly converted from "N-element array of char *" to "pointer to char *", and what foo actually receives is a pointer to the first element of the array - it's effectively the same as writing
foo( &ptrs[0] );
The prototype can be written as either
void foo( char **ptrs )
or
void foo( char *ptrs[] )
or
void foo( char *ptrs[N] )
In a function parameter declaration, array declarators are "adjusted" to be pointer declarators - IOW, T a[N] and T a[] are both interpreted to mean T *a. This is only true in a function parameter declaration, though.
As a matter of style...
In C, the cast on malloc is unnecessary1, and under C89 it can suppress a useful diagnostic if you forget to include stdlib.h or otherwise don't have a declaration for malloc (or calloc or realloc) in scope. Under C99 and later, you'll get a diagnostic for not having a declaration, but C89 still allowed implicit int declarations, and the cast will prevent the compiler from yelling at you because int and pointer types are not compatible. I bring this up because MS's support for C past C89 is a bit spotty.
To minimize your maintenance burden, it's better to avoid explicitly naming types in a malloc call. You can rewrite
SrcArr = (char **)malloc(999999 * sizeof(char *));
as
SrcArr = malloc( 999999 * sizeof *SrcArr ); // you sure you need that many elements??!
Since SrcArr has type char **, the expression *SrcArr has type char *, so sizeof *SrcArr is the same as sizeof (char **). In general, a malloc call can be written
T *p = malloc( N * sizeof *p );
or
T *p;
...
p = malloc( N * sizeof *p );
The same is true for calloc and realloc.
This is not the cast in C++, since C++ doesn't allow implicit conversion from void * to other pointer types, but if you're writing C++ you shouldn't be using malloc anyway.
I'm pretty bad at remembering C rules with structs. Basically, I have a struct like this:
typedef struct {
char* ptr;
int size;
} Xalloc_struct;
Where the char* ptr will only be one character max.
In my program, I have to allocate and free memory to a fake disk (declared globally as char disk[100];) using my own functions:
char disk[100];
void disk_init() {
for(int i = 0; i < 100; ++i) {
disk[i] = memory[i] = 0;
}
}
struct Xalloc_struct* Xalloc(int size) {
// error checking
// ...
// run an algorithm to get a char* ptr back to a part of the global disk
// array, where index i is the index where content at disk[i] starts
char* ptr = &disk[i];
struct Xalloc_struct *ret = malloc(sizeof(struct Xalloc_struct));
ret->size = size;
ret->ptr = malloc(sizeof(char));
ret->ptr = ptr;
return ret;
}
int Xfree(void* ptr) {
struct Xalloc_struct* p = (struct Xalloc_struct*) ptr;
int size = p->size;
int index = *(p->ptr);
// .. more stuff here that uses the index of where p->ptr points to
free(p->ptr);
free(p);
return 0;
}
int main() {
disk_init();
struct Xalloc_struct* x = Xalloc(5);
Xfree(x);
return 0;
}
When this compiles I get quite a few errors:
error: invalid application of ‘sizeof’ to incomplete type ‘struct Xalloc_struct’
struct Xalloc_struct *ret = malloc(sizeof(struct Xalloc_struct));
^
error: dereferencing pointer to incomplete type
ret->size = size;
^
error: dereferencing pointer to incomplete type
free(x->ptr);
^
error: dereferencing pointer to incomplete type
int size = cast_ptr->size;
^
error: dereferencing pointer to incomplete type
int free_ptr = *(cast_ptr->ptr);
^
So, how should I be allocating and deallocating these structs? And how can I modify / edit what they contain?
First problem is Xalloc_struct is a type, not the name of a struct. You declared that type with this:
typedef struct {
char* ptr;
int size;
} Xalloc_struct;
typedef is of the form typedef <type name or struct definition> <name of the type>. So you declared the type Xalloc_struct to be struct { char *ptr; int size; }.
That means you use it like any other type name: Xalloc_struct somevar = ...;.
Had you declared the struct with a name...
struct Xalloc_struct {
char* ptr;
int size;
};
Then it would be struct Xalloc_struct somevar = ...; as you have.
The rule of thumb when allocating memory for an array (and a char * is an array of characters) is you allocate sizeof(type) * number_of_items. Character arrays are terminated with a null byte, so for them you need one more character.
Xalloc_struct *ret = malloc(sizeof(Xalloc_struct));
ret->ptr = malloc(sizeof(char) * num_characters+1);
But if you're only storing one character, there's no need for an array of characters. Just store one character.
typedef struct {
char letter;
int size;
} Xalloc_struct;
Xalloc_struct *ret = malloc(sizeof(Xalloc_struct));
ret->letter = 'q'; /* or whatever */
But what I think you're really doing is storing a pointer to a spot in the disk array. In that case, you don't malloc at all. You just store the pointer like any other pointer.
typedef struct {
char* ptr;
int size;
} Xalloc_struct;
Xalloc_struct *ret = malloc(sizeof(Xalloc_struct));
ret->ptr = &disk[i];
Then you can read that character with ret->ptr[0].
Since you didn't allocate ret->ptr do not free it! That will cause a crash because disk is in stack memory and cannot be free'd. If it were in heap memory (ie. malloc) it would probably also crash because it would try to free in the middle of an allocated block.
void Xalloc_destroy(Xalloc_struct *xa) {
free(xa);
}
Here's how I'd do it.
#include <stdio.h>
#include <stdlib.h>
char disk[100] = {0};
typedef struct {
char *ptr;
int idx;
} Disk_Handle_T;
static Disk_Handle_T* Disk_Handle_New(char *disk, int idx) {
Disk_Handle_T *dh = malloc(sizeof(Disk_Handle_T));
dh->idx = idx;
dh->ptr = &disk[idx];
return dh;
}
static void Disk_Handle_Destroy( Disk_Handle_T *dh ) {
free(dh);
}
int main() {
Disk_Handle_T *dh = Disk_Handle_New(disk, 1);
printf("%c\n", dh->ptr[0]); /* null */
disk[1] = 'c';
printf("%c\n", dh->ptr[0]); /* c */
Disk_Handle_Destroy(dh);
}
What you are attempting to accomplish is a bit bewildering, but from a syntax standpoint, your primary problems are treating a typedef as if it were a formal struct declaration, not providing index information to your Xalloc function, and allocating ret->ptr where you already have a pointer and storage in disk.
First, an aside, when you are specifying a pointer, the dereference operator '*' goes with the variable, not with the type. e.g.
Xalloc_struct *Xalloc (...)
not
Xalloc_struct* Xalloc (...)
Why? To avoid the improper appearance of declaring something with a pointer type, (where there is no pointer type just type) e.g.:
int* a, b, c;
b and c above are most certainly NOT pointer types, but by attaching the '*' to the type it appears as if you are trying to declare variables of int* (which is incorrect).
int *a, b, c;
makes it much more clear you intend to declare a pointer to type int in a and two integers b and c.
Next, in Xfree, you can, but generally do not want to, assign a pointer type as an int (storage size issues, etc.) (e.g. int index = *(p->ptr);) If you need a reference to a pointer, use a pointer. If you want the address of the pointer itself, make sure you are using a type large enough for the pointer size on your hardware.
Why are you allocating storage for ret->ptr = malloc(sizeof(char));? You already have storage in char disk[100]; You get no benefit from the allocation. Just assign the address of the element in disk to ptr (a pointer can hold a pointer without further allocation) You only need to allocate storage for ret->ptr if you intend to use the memory you allocate, such as copying a string or multiple character to the block of memory allocated to ret->ptr. ret->ptr can store the address of an element in data without further allocation. (it's unclear exactly what you intend here)
You are free to use a typedef, in fact it is good practice, but when you specify a typedef as you have, it is not equivalent to, and cannot be used, as a named struct. That is where your incomplete type issue arises.
All in all, it looks like you were trying to do something similar to the following:
#include <stdio.h>
#include <stdlib.h>
typedef struct {
char* ptr;
int size;
} Xalloc_struct;
char disk[100] = "";
Xalloc_struct *Xalloc (int size, int i) {
char *ptr = &disk[i];
Xalloc_struct *ret = malloc (sizeof *ret);
ret->size = size;
// ret->ptr = malloc (sizeof *(ret->ptr)); /* you have a pointer */
ret->ptr = ptr;
return ret;
}
int Xfree (void *ptr) {
Xalloc_struct *p = (Xalloc_struct *) ptr;
// int size = p->size; /* unused */
// int index = *(p->ptr); /* what is this ?? */
// .. more stuff here that uses the index of where p->ptr points to
// free (p->ptr);
free (p);
return 0;
}
int main (void) {
int i = 0;
Xalloc_struct *x = Xalloc (5, i++);
Xfree(x);
return 0;
}
Look at the difference in how the typedef is used and let me know if you have any questions.
I'm writing a function that reverses a cstring not in place but returns the reversed cstring. What exactly should the return type be?
#include <stdio.h>
#include <string.h>
const char* reverStr(const char *str)
{
char revStr[strlen(str)];
int i;
for(i = strlen(str)-1; i >= 0; i--)
revStr[strlen(str)-1-i] = str[i];
printf("returned value should be %s\n", revStr);
return revStr;
}
int main()
{
char aStr[] = "hello";
char aStr2[] = "goodbye";
printf("%s %s", aStr, aStr2);
char* tmp = reverStr(aStr);//tmp now has garbage
printf("\n%s", tmp);
printf(" %s", aStr);
return 0;
}
Gives
warning: function returns address of local variable [enabled by default]|
warning: initialization discards 'const' qualifier from pointer target type [enabled by default]|
I tried changing char* tmp to char tmp[] but it wouldn't compile. It confuses me when I should use an array and when I should use a pointer.
revStr is an array and ceases to exist after reverStr function exits. For more please read:
Where is the memory allocated when I create this array? (C)
const char* reverStr(const char *str)
{
char revStr[strlen(str)];
return revStr; /* Problem - revStr is a local variable trying to access this address from another function will be erroneous*/
}
const char* reverStr(const char *str)
{
const char * revStr = str;
return revStr; //ok
}
A modifiable l-value cannot have an array type. An l-value is an expression which can come on the left side of an assignment. You use an array when you want to declare lots of variables of the same type and you can index it easily since its layout will be in a sense contiguous.
You use pointers when you want to keep changing the values of the address where you variable points to.
You can do this:
char * p = "test";
p = "new";
But you cannot do this:
char p[] = "test";
char *p1 ="test1";
p = p1; //error
Because their (arrays and pointers) types are not the same and the array p is a non-modifiable l-value.
Here is your fixed code. I tried to make less modifications.
char revStr[strlen(str)]; allocates a local variable(an array) and when you are out of the scope of the reverStr function, its memory is released, which will lead any further usage of its pointer to be UB(segfault in most cases).
A correct way is to allocate the string on the heap and return its pointer like this
char* x = (char*)malloc(strlen(str));
...
return x;
This requires user to be responsible to free the memory. Or you could pass another parameter to your function for the result string.
I think you should use malloc to allocate a new string.
const char* reverStr(const char *str)
{
char *revStr;//using pointer
int i;
revStr = (char*)malloc(strlen(str));//dynamic allocation
for(i = strlen(str)-1; i >= 0; i--)
revStr[strlen(str)-1-i] = str[i];
printf("returned value should be %s\n", revStr);
return revStr;
}
An array is a pointer point to the head of continuous memory.
for example:
int a[] = {1,2,3};
The address in memory maybe:
--1000
|1|
--1004
|2|
--1008
|3|
--1012
1000, 1004, and 1012 are the value of address in memory.
Thus, the value of array a should be 1000.
printf("%d",a);// Yes, you can do it and you may get the value of 1000.
Also, you can use the following code.
int a[] = {1,2,3};
int *b;
b= a;
printf("%d",b[1]);// you will get "2".
You can consider that pointer is a set and array is in the set.
Therefore, you can NOT do this;
int a[] = {1,2,3};
int c = 0;
int *b = &c;
a = b;//error