#define STRING(s) (((String*)s)-1)
what in the world is (((String*)s)-1)?
typedef
struct String {
int length;
int capacity;
unsigned check;
char ptr[0];
} String;
You're casting s to a String *. Then you're subtracting one from it (making it point to the previous whatever).
Anything more specific would need to know the definition of String - but (WILD SPECULATION) I would guess the application uses dual VB/C-style strings (null terminated, preceded by the length), and this function changes it from a form suitable for C functions (pointer to the first character) into one usable for the other type (pointer to the length).
Mechanically, the macro works as others have already described it. Semantically, though, you can think of this as a form of casting from char *s to String *s.
The String structure is the header of a counted string, ie, one where you know the total length without having to scan for a NUL byte. This particular version also keeps the total allocated. You would create one as follows:
struct String *str = malloc(sizeof(*s) + maxlen);
str->length = 0;
str->capacity = maxlen;
str->checked = /* ??? */;
There should be some assorted functions around somewhere to manipulate these counted strings.
The macro itself is a hack to go from a plain char *, assumed to point to the first character of the a String as allocated above, back to a String *. It would be used something like this:
/* allocate str as above */
char *s = str->p;
Now, through a chain of function calls or returns, you somehow loose track of the String structure containing s, and you need to find it again. You write:
String *str2 = STRING(s);
This is not a particularly good way to implement a counted string in C, but it demonstrates a technique that one sees from time to time.
Others have answered your question. The technique of declaring ptr inside struct String with zero size is called "the struct hack", and wasn't portable until C99 (although it was widely used even before C99, and seems to work everywhere). The idea is that ptr uses 0 bytes, so if you have a pointer to ptr, and want one to the original struct, you would use the STRING macro. You are subtracting the size of the struct from the address of the ptr member, and thus getting the start address of the struct.
A better way to get the start address of a struct given a pointer to any of its members is to use offsetof() macro defined in stddef.h. offsetof(struct type, member), as the name implies, gives the offset of member in struct type:
#define STRING(x) ((String *)(((char *)(x) - offsetof(struct String, ptr))))
Then you can do:
#include <stddef.h>
#include <stdlib.h>
#include <assert.h>
typedef struct String {
int length;
int capacity;
unsigned check;
char ptr[0];
} String;
#define STRING(x) ((String *)(((char *)(x) - offsetof(struct String, ptr))))
int main(void)
{
String *s = malloc(sizeof *s + 100);
String *t;
char *mystring = s->ptr;
t = STRING(mystring);
assert(t == s);
return EXIT_SUCCESS;
}
offsetof() is defined in stddef.h.
Note that in C99, "struct hack" would declare ptr inside the struct as:
char ptr[];
i.e., without a size.
(String*) = type cast to pointer to String object,
s = the string,
-1 = point to one String object's length before in the memory block
Don't know why the macro is made this way. Possibly the definition of String requires this, but that's just a wild guess.
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
I have this code:
char** SplitToWords(char* str);
int main()
{
char** wordarr;
char str[] = "This is a sentence";
wordarr = SplitToWords(str);
return 0;
}
After the main comes the function implementation.
I am not sure the following does what I want it to do (i.e. receive an array of strings from a function):
wordarr = SplitToWords(str);
I somehow managed to convince the compiler that it's ok, but I assume it just does something else.
If it does, how do I find out the length of the array (the number of strings in it).
Thanks
I'll try to quickly visit all aspects you might not yet fully understand:
A string in C is described as a contiguous sequence of chars, ending with a char of value 0 (as a literal: '\0'). It is not a first class object, therefore hasn't its own type. So what you use to hold a string is an array of char. Therefore, taking your question by the word, "receive an array of strings from a function" is not possible.
An array is a contiguous sequence of objects of the same type. In C, the identifier of an array doesn't have a value itself; when it's evaluated, it decays as a pointer to the array's first element instead. This is especially important when passing arrays to functions or returning them from functions -- you can't actually pass the array, you always pass a pointer
e.g. you could write:
char x[] = "foo"; // initialize a char array from a string literal
char *xp = x; // here, x evaluates as a pointer to the first element of the array
You already use pointer types for your function's argument and return value, I just think it's quite important to understand what happens entirely.
You write char** SplitToWords(char* str); and ask whether this returns an "array of strings" -- well, sort of, as you should understand after reading 1. and 2. -- What it does is returning a pointer to char *. This pointer could be a pointer to the first element of an array. So in this case, it would return a pointer to an array of char * pointers. Each of these pointers could itself be a pointer to an array of chars, therefore point to a string. But what's very important is to understand you never return an array, you always return a pointer to it. It's so important because:
You might get the idea to do something like this:
char** SplitToWords(char* str)
{
char *words[16];
// code to fill `words` with pointers to the actual words
return words; // WRONG!
}
Here, because you're not returning the array words but a pointer to it (see point 2), you return a pointer to an object that no longer exists. words is in the scope of your function and has automatic storage duration, that means it only lives as long as the execution is inside of the function. One solution would be to declare words with the static storage class specifier. This way, it lives for the entire execution time of the program. But be aware that this also means there's only a single instance ever, it's always the same object. This will be a major headache for threaded programs, for example. The other way around is to dynamically allocate words using malloc(). But then, the caller of the function must free() it later.
As for your second question, how to let the caller know the number of words -- it's in the comments already, but just for completeness, a typical approach to solve this is to append another entry that is a NULL pointer. So the caller can iterate over the pointers until it finds NULL.
Regarding your comment, of course you can create the array outside the function and pass a pointer to the function, so the function only fills it. This is a common idiom in C (e.g. think about fgets(), which takes a pointer to the char array that's filled with a string by the function).
Functions working this way will need an additional size_t parameter, so they know the size of the array they should fill through the pointer, otherwise you'd have the risk of buffer overflows (this is why gets() was finally removed from the C standard). If you decide that the caller provides the storage, your function should have this prototype:
// returns the number of words found, up to `nwords`
size_t SplitToTwords(char **words, size_t nwords, char *str);
It should be called e.g. like this:
char *words[16];
size_t nwords = SplitToWords(words, 16, "the quick brown fox"); // returns 4
Remember that the strings holding the words themselves need storage as well. You can either manipulate the bytes in str to insert a '\0' after each word, overwriting the first whitespace character (this is what strtok() does) or you can copy the words to new strings, but then you would have to malloc() each of them again and the caller has to free() them later.
Yes, you could solve it by using a function with return value char **. However, there's no way to find out how many words there are afterwards.
You can solve this by allocating one more element for the return pointer and set it to NULL. Then you can get the number of words with this code:
wordarr = SplitToWords(str);
char **ptr=wordarr;
int noWords=0;
while(!*(ptr+noWords))
noWords++;
But if you want to return multiple data in C, you either need to define a return struct or using return arguments. In this case, it could look like this for the first option:
typedef struct wordList {
char **wordarr;
int noWords;
}
wordList SplitToWords(char* str);
And the second:
char** SplitToWords(char* str, int *noWords);
or
void SplitToWords(char* str, char*** wordarr, int *noWords);
Note that there's three *. That's because we want it to be a pointer to char **
#include "stdafx.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAXSTRINGS 5000
int main(int argc, char *argv[]) {
char *stringTable[MAXSTRINGS];
char sentence[] = "This is a sentence";
char *token = NULL;
int i = 0;
while ((token = strtok(token == NULL ? sentence : NULL, " ")) != NULL)
{
printf("%s\n\r", token);
stringTable[i] = (char *)malloc(strlen(token) + 1); //have no "plain" C compiler - VS C++ used so cast needed :)
strcpy(stringTable[i++], token);
}
stringTable[i] = NULL; // if you need to iterate through later
printf("%d tokens found\n\r", i);
for (int y = 0; y < i; y++)
free(stringTable[y]);
}
I am declaring the global variable sym this way:
extern char *sym; // in header.h
char *sym = NULL; // in main.c
//Then allocate memory within main()
char (*sym)[DIM_2_SYM][BUF_16] = malloc(sizeof(char[N][DIM_2_SYM][BUF_16]));
memset(sym, 0, sizeof sym);
However when try to pass a string sym[i][j] to strdup get error: subscripted value is neither array nor pointer nor vector. It seems that I am passing a value instead of const char * but do not understand why:
char buf*;
buf = strdup(sym[1][0]);
Presumably because sym defined in the main, is shadowing sym defined above main in file scope.
The argument passed to strdup is the first sym which is of type char*.
The size of sym, used here:
memset(sym, 0, sizeof sym);
is the size of the pointer, so the memset doesn't zero the entire object.
Use the same method as in the allocation:
memset(sym, 0, sizeof(char[N][DIM_2_SYM][BUF_16]));
or even better store the value in an object so you won't repeat yourself:
const size_t sym_bytes = sizeof(char[N][DIM_2_SYM][BUF_16]);
Since the array sizes involved in the calculation are all constants, simply defined the global variable as:
extern char sym[N][DIM_2_SYM][BUF_16]; // in header.h
char sym[N][DIM_2_SYM][BUF_16];
and no allocation is needed.
Or you could allocate, in which case, only the outer size of the object can be changed:
extern char ( *sym )[DIM_2_SYM][BUF_16]; // in header.h
char ( *sym )[DIM_2_SYM][BUF_16] = NULL;
int main( void )
{
sym = malloc( sizeof( *sym ) * N );
}
and N doesn't have to be constant.
The code presented is really, really confused.
First of all, these are fine and consistent in themselves:
extern char *sym; // in header.h
char *sym = NULL; // in main.c
This, too, is fine in and of itself (in C):
char (*sym)[DIM_2_SYM][BUF_16] = malloc(sizeof(char[N][DIM_2_SYM][BUF_16]));
Note, however, that it declares a local sym, different from the file-scoped one, with a different type.
This is legal, but it does not do what you want:
memset(sym, 0, sizeof sym);
sym is a pointer; its size is the size of a pointer, probably 4 or 8 bytes. Supposing that you want to zero-fill the whole dynamically-allocated space, the easiest thing to do is probably to use calloc() instead of malloc() to perform the allocation. It does the zero fill for you.
Given the error message, the sym that is in scope for these statements:
char buf*;
buf = strdup(sym[1][0]);
is the file-scope one, whose type is char *. For that sym, sym[1] designates a char, and the second indexing operator is inapplicable to that. On the other hand, that code would be acceptable for the local sym whose allocation you present.
You haven't presented enough information for me to suggest a fix, but do be aware that VLAs cannot have file scope. You may therefore need to approach your problem differently.
In a program I am writing I made a Tokenize struct that says:
TokenizerT *Tokenize(TokenizerT *str) {
TokenizerT *tok;
*tok->array = malloc(sizeof(TokenizerT));
char * arr = malloc(sizeof(50));
const char *s = str->input_strng;
int i = 0;
char *ds = malloc(strlen(s) + 1);
strcpy(ds, s);
*tok->array[i] = strtok(ds, " ");
while(*tok->array[i]) {
*tok->array[++i] = strtok(NULL, " ");
}
free(ds);
return tok;
}
where TokenizeT is defined as:
struct TokenizerT_ {
char * input_strng;
int count;
char **array[];
};
So what I am trying to do is create smaller tokens out of a large token that I already created. I had issues returning an array so I made array part of the TokenizerT struct so I can access it by doing tok->array. I am getting no errors when I build the program, but when I try to print the tokens I get issues.
TokenizerT *ans;
TokenizerT *a = Tokenize(tkstr);
char ** ab = a->array;
ans = TKCreate(ab[0]);
printf("%s", ans->input_strng);
TKCreate works because I use it to print argv but when i try to print ab it does not work. I figured it would be like argv so work as well. If someone can help me it would be greatl appreciated. Thank you.
Creating the Tokenizer
I'm going to go out on a limb, and guess that the intent of:
TokenizerT *tok;
*tok->array = malloc(sizeof(TokenizerT));
char * arr = malloc(sizeof(50));
was to dynamically allocate a single TokenizerT with the capacity to contain 49 strings and a NULL endmarker. arr is not used anywhere in the code, and tok is never given a value; it seems to make more sense if the values are each shifted one statement up, and corrected:
// Note: I use 'sizeof *tok' instead of naming the type because that's
// my style; it allows me to easily change the type of the variable
// being assigned to. I leave out the parentheses because
// that makes sure that I don't provide a type.
// Not everyone likes this convention, but it has worked pretty
// well for me over the years. If you prefer, you could just as
// well use sizeof(TokenizerT).
TokenizerT *tok = malloc(sizeof *tok);
// (See the third section of the answer for why this is not *tok->array)
tok->array = malloc(50 * sizeof *tok->array);
(tok->array is not a great name. I would have used tok->argv since you are apparently trying to produce an argument vector, and that's the conventional name for one. In that case, tok->count would probably be tok->argc, but I don't know what your intention for that member is since you never use it.)
Filling in the argument vector
strtok will overwrite (some) bytes in the character string it is given, so it is entirely correct to create a copy (here ds), and your code to do so is correct. But note that all of the pointers returned by strtok are pointers to character in the copy. So when you call free(ds), you free the storage occupied by all of those tokens, which means that your new freshly-created TokenizerT, which you are just about to return to an unsuspecting caller, is full of dangling pointers. So that will never do; you need to avoid freeing those strings until the argument vector is no longer needed.
But that leads to another problem: how will the string be freed? You don't save the value of ds, and it is possible that the first token returned by strtok does not start at the beginning of ds. (That will happen if the first character in the string is a space character.) And if you don't have a pointer to the very beginning of the allocated storage, you cannot free the storage.
The TokenizerT struct
char is a character (usually a byte). char* is a pointer to a character, which is usually (but not necessarily) a pointer to the beginning of a NUL-terminated string. char** is a pointer to a character pointer, which is usually (but not necessarily) the first character pointer in an array of character pointers.
So what is char** array[]? (Note the trailing []). "Obviously", it's an array of unspecified length of char**. Because the length of the array is not specified, it is an "incomplete type". Using an incomplete array type as the last element in a struct is allowed by modern C, but it requires you to know what you're doing. If you use sizeof(TokenizerT), you'll end up with the size of the struct without the incomplete type; that is, as though the size of the array had been 0 (although that's technically illegal).
At any rate, that wasn't what you wanted. What you wanted was a simple char**, which is the type of an argument vector. (It's not the same as char*[] but both of those pointers can be indexed by an integer i to return the ith string in the vector, so it's probably good enough.)
That's not all that's wrong with this code, but it's a good start at fixing it. Good luck.
My structure looks as follows:
typedef struct {
unsigned long attr;
char fileName[128];
} entity;
Then I try to assign some values but get an error message...
int attribute = 100;
char* fileNameDir = "blabla....etc";
entity* aEntity;
aEntity->attr = attributes;
aEntity->fileName = fileNameDir;
Compiler tells me:
Error: #137: expression must be a modifiable lvalue
aEntity->fileName = fileNameDir;
Why cant I assign here this character to the one in the structure?
Thanks
You're treating a char[] (and a char*, FTM) as if it was a string. Which is is not. You can't assign to an array, you'll have to copy the values. Also, the length of 128 for file names seems arbitrary and might be a potential source for buffer overflows. What's wrong with using std::string? That gets your rid of all these problems.
You're defining a pointer to some entity, don't initialize it, and then use it as if at the random address it points to was a valid entity object.
There's no need to typedef a struct in C++, as, unlike to C, in C++ struct names live in the same name space as other names.
If you absolutely must use the struct as it is defined in your question (it is pre-defined), then look at the other answers and get yourself "The C Programming Language". Otherwise, you might want to use this code:
struct entity {
unsigned long attr;
std::string fileName;
};
entity aEntity;
aEntity.attr = 100;
aEntity.filename = "blabla....etc";
You can't assign a pointer to an array. Use strncpy() for copying the string:
strncpy( aEntity->fileName, fileNameDir, 128 );
This will leave the destination not null-terminated if the source is longer than 128. I think the best solution is to have a bigger-by-one buffer, copy only N bytes and set the N+1th byte to zero:
#define BufferLength 128
typedef struct {
unsigned long attr;
char fileName[BufferLength + 1];
} entity;
strncpy( aEntity->FileName, fileNameDir, BufferLength );
*( aEntity->FileName + BufferLength ) = 0;
You should be copying the filename string, not changing where it points to.
Are you writing C or C++? There is no language called C/C++ and the answer to your question differs depending on the language you are using. If you are using C++, you should use std::string rather than plain old C strings.
There is a major problem in your code which I did not see other posters address:
entity* aEntity;
declares aEntity (should be anEntity) as a pointer to an entity but it is not initialized. Therefore, like all uninitialized pointers, it points to garbage. Hence:
aEntity->attr = attributes;
invokes undefined behavior.
Now, given a properly initialized anEntity, anEntity->fileName is an array, not a pointer to a character array (see question 6.2 in the C FAQ list). As such, you need to copy over the character string pointed to by fileNameDir to the memory block reserved for anEntity->fileName.
I see a lot of recommendations to use strncpy. I am not a proponent of thinking of strncpy as a safer replacement for strcpy because it really isn't. See also Why is strncpy insecure?
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct st_entity {
unsigned long attr;
char fileName[FILENAME_MAX + 1];
} entity;
int main(void) {
int status = EXIT_FAILURE;
unsigned long attribute = 100;
char *fileNameDir = "blabla....etc";
entity *anEntity = malloc(sizeof(*anEntity));
if ( anEntity ) {
anEntity->attr = attribute;
anEntity->fileName[0] = '\0';
strncat(anEntity->fileName, fileNameDir, sizeof(anEntity->fileName) - 1);
printf("%lu\n%s\n", anEntity->attr, anEntity->fileName);
status = EXIT_SUCCESS;
}
else {
fputs("Memory allocation failed", stderr);
}
return status;
}
See strncat.
You're trying to use char* as if it was a string, which it is not. In particular, you're telling the compiler to set filename, a 128-sized char array, to the memory address pointed by fileNameDir.
Use strcpy: http://cplusplus.com/reference/clibrary/cstring/strcpy/
You can't assign a pointer to char to a char array, they're not compatible that way, you need to copy contents from one to another, strcpy, strncpy...
Use strncpy():
strncpy( aEntity->fileName, fileNameDir, sizeof(entity.fileName) );
aEntity.fileName[ sizeof(entity.fileName) - 1 ] = 0;
The strncpy() function is similar,
except that not more than n bytes of
src are copied. Thus, if there is no
null byte among the first n bytes of
src, the result will not be
null-terminated. See man page.
1) The line char* fileNameDir = "blabla....etc" creates a pointer to char and assigns the pointer an address; the address in this case being the address of the text "blabla....etc" residing in memory.
2) Furthermore, arrays (char fileName[128]) cannot be assigned to at all; you can only assign to members of an array (e.g. array[0] = blah).
Knowing (1) and (2) above, it should be obvious that assigning an address to an array is not a valid thing to do for several reasons.
What you must do instead is to copy the data that fileNameDir points to, to the array (i.e. the members of the array), using for example strncpy.
Also note that you have merely allocated a pointer to your struct, but no memory to hold the struct data itself!
First of all, is this supposed to be C or C++? The two are not the same or freely interchangeable, and the "right" answer will be different for each.
If this is C, then be aware you cannot assign strings to arrays using the '=' operator; you must either use strcpy() or strncpy():
/**
* In your snippet above, you're just declaring a pointer to entity but not
* allocating it; is that just an oversight?
*/
entity *aEntity = malloc(sizeof *aEntity);
...
strcpy(aEntity->fileName, fileNameDir);
or
strncpy(aEntity->fileName, fileNameDir, sizeof aEntity->fileName);
with appropriate checks for a terminating nul character.
If this is C++, you should be using the std::string type for instead of char* or char[]. That way, you can assign string data using the '=' operator:
struct entity {unsigned long attr; std::string fileName};
entity *aEntity = new entity;
std::string fileNameDir = "...";
...
entity->fileName = fileNameDir;
The major problem is that you declared a pointer to a struct, but allocated no space to it (unless you left some critical code out). And the other problems which others have noted.
The problem lies in the fact that you cannot just use a pointer without initialising it to a variable of that same datatype, which in this is a entity variable. Without this, the pointer will point to some random memory location containing some garbage values. You will get segmentation faults when trying to play with such pointers.
The second thing to be noted is that you can't directly assign strings to variables with the assignment operator(=). You have to use the strcpy() function which is in the string.h header file.
The output of the code is:
100 blabla......etc
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct
{
unsigned long attr;
char fileName[128];
} entity;
void main()
{
unsigned long int attribute = 100;
char *fileNameDir = "blabla....etc";
entity struct_entity;
entity *aEntity = &struct_entity;
aEntity->attr = attribute;
strcpy(aEntity->fileName, fileNameDir);
printf("%ld %s", struct_entity.attr, struct_entity.fileName);
}
For char fileName[128], fileName is the array which is 128 char long. you canot change the fileName.
You can change the content of the memory that filename is pointing by using strncpy( aEntity->fileName, fileNameDir, 128 );
I've been instructed to write a model strdup by creating a String struct on the heap the holds a copy of the source. I think I have successfully coded the strdup, but I'm not sure if I've created a Struct on the heap...
typedef
struct String {
int length;
int capacity;
unsigned check;
char ptr[0];
} String;
char* modelstrdup(char* src){
int capacity =0, length=0, i = 0 ;
char *string;
while ( src[length] != '\0'){
length++;
}
capacity = length;
string = malloc(sizeof(String) + capacity + 1);
while ( i < length ){
string[i] = src[i];
i++;
}
string[i+1] = '\0';
return string;
}
Yes, you've created a struct on the heap. You haven't populated it correctly, and you are going to face problems deleting it - I'm not sure whether the homework covered that or not. As it stands, you're more likely to get memory corruption or, if you're lucky, a memory leak than to release one of these strings.
Code that works with standard C89 and C99
Your code, somewhat fixed up...
typedef
struct String {
int length;
int capacity;
char *ptr;
} String;
char* modelstrdup(char* src){
int length = strlen(src);
char *space = malloc(sizeof(String) + length + 1);
//String *string = space; // Original code - compilers are not keen on it
String *string = (String *)space;
assert(space != 0);
string->ptr = space + sizeof(String); // or sizeof(*string)
string->length = length;
string->capacity = length + 1;
strcpy(string->ptr, src);
return string->ptr;
}
This code will work in C89 as well as C99 (except for the C99/C++ comments). You can probably optimize it to work with the 'struct hack' (saves a pointer in the structure - but only if you have a C99 compiler). The assert is sub-optimal error handling. The code doesn't defend itself against a null pointer for input. In this context, neither the length nor the capacity provides any benefit - there must be other functions in the suite that will be able to make use of that information.
As already intimated, you are going to face problems deleting the string structure when the value handed back is not a pointer to the string. You have some delicate pointer adjustments to make.
Code that works with standard C99 only
In C99, section 6.7.2.1 paragraph 16 describes 'flexible array members':
As a special case, the last element of a structure with more than one named member may
have an incomplete array type; this is called a flexible array member. With two
exceptions, the flexible array member is ignored. First, the size of the structure shall be
equal to the offset of the last element of an otherwise identical structure that replaces the
flexible array member with an array of unspecified length.106) Second, when a . (or ->)
operator has a left operand that is (a pointer to) a structure with a flexible array member
and the right operand names that member, it behaves as if that member were replaced
with the longest array (with the same element type) that would not make the structure
larger than the object being accessed; the offset of the array shall remain that of the
flexible array member, even if this would differ from that of the replacement array. If this
array would have no elements, it behaves as if it had one element but the behavior is
undefined if any attempt is made to access that element or to generate a pointer one past
it.
106 The length is unspecified to allow for the fact that implementations may give array members different
alignments according to their lengths.
Using a 'flexible array member', your code could become:
typedef
struct String {
int length;
int capacity;
char ptr[];
} String;
char* modelstrdup(char* src){
int length = strlen(src);
String *string = malloc(sizeof(String) + length + 1);
assert(string != 0);
string->length = length;
string->capacity = length + 1;
strcpy(string->ptr, src);
return string->ptr;
}
This code was accepted as clean by GCC 4.0.1 apart from a declaration for the function (options -Wall -Wextra). The previous code needs a cast on 'String *string = (String *)space;' to tell the compiler I meant what I said; I've now fixed that and left a comment to show the original.
Using the 'struct hack'
Before C99, people often used the 'struct hack' to handle this. It is very similar to the code shown in the question, except the dimension of the array is 1, not 0. Standard C does not allow array dimensions of size zero.
typedef struct String {
size_t length;
size_t capacity;
char ptr[1];
} String;
char* modelstrdup(char* src)
{
size_t length = strlen(src);
String *string = malloc(sizeof(String) + length + 1);
assert(string != 0);
string->length = length;
string->capacity = length + 1;
strcpy(string->ptr, src);
return string->ptr;
}
Code that uses a GCC non-standard extension to C89 and C99
The zero-size array notation is accepted by GCC unless you poke it hard - specify the ISO C standard and request pedantic accuracy. This code, therefore, compiles OK unless you get to use gcc -Wall -Wextra -std=c99 -pedantic:
#include <assert.h>
#include <stdlib.h>
#include <string.h>
typedef
struct String {
int length;
int capacity;
char ptr[0];
} String;
char* modelstrdup(char* src){
int length = strlen(src);
String *string = malloc(sizeof(String) + length + 1);
assert(string != 0);
string->length = length;
string->capacity = length + 1;
strcpy(string->ptr, src);
return string->ptr;
}
However, you should not be being trained in non-standard extensions to the C language before you have a thorough grasp of the basics of standard C. That is simply unfair to you; you can't tell whether what you're being told to do is sensible, but your tutors should not be misguiding you by forcing you to use non-standard stuff. Even if they alerted you to the fact that it is non-standard, it is not fair to you. C is hard enough to learn without learning tricksy stuff that is somewhat compiler specific.
You have allocated some memory on the heap, but you're not using it as if it were your structure. The string variable in your function is of type char *, not of type struct String. I think you're duplicating the functionality of strdup() reasonably enough, but I don't understand the reason for the structure.
Note: You should probably check your call to malloc() for failure, and return appropriately. The man page for strdup() should explains exactly what your function should be doing.
You have. Malloc, new, etc all use the heap.
Yes, malloc returns memory on the heap.