C -- Basic Struct questions - c

So I'm trying to learn C right now, and I have some basic struct questions I'd like to clear up:
Basically, everything centers around this snippet of code:
#include <stdio.h>
#include <stdlib.h>
#define MAX_NAME_LEN 127
typedef struct {
char name[MAX_NAME_LEN + 1];
unsigned long sid;
} Student;
/* return the name of student s */
const char* getName (const Student* s) { // the parameter 's' is a pointer to a Student struct
return s->name; // returns the 'name' member of a Student struct
}
/* set the name of student s
If name is too long, cut off characters after the maximum number of characters allowed.
*/
void setName(Student* s, const char* name) { // 's' is a pointer to a Student struct | 'name' is a pointer to the first element of a char array (repres. a string)
s->name = name;
}
/* return the SID of student s */
unsigned long getStudentID(const Student* s) { // 's' is a pointer to a Student struct
return s->sid;
}
/* set the SID of student s */
void setStudentID(Student* s, unsigned long sid) { // 's' is a pointer to a Student struct | 'sid' is a 'long' representing the desired SID
s->sid = sid;
}
I've commented up the code in an attempt to solidify my understanding of pointers; I hope they're all accurate.
So anyway, I have a feeling that setName and setStudentID aren't correct, but I'm not exactly sure why. Can someone explain? Thanks!
EDIT:
char temp
int i;
for (i = 0, temp = &name; temp != '\0'; temp++, i++) {
*((s->name) + i) = temp;

You are not copying the full name array with this
void setName(Student* s, const char* name) {
s->name = name;
}
try this
strcpy(s->name,name);
to copy this string to your structs array. You cant simply assign a pointer argument to an array variable like you have currently. You need to copy each character pointed to by name to the elements of your array s->name. This is what strcpy will do - it copies elements from the source to the destination until it finds a terminating null character.
EDIT: Alternatively you could use strncpy as suggested in a comment. Check this question and its answers to see why some people think this is a good idea Why should you use strncpy instead of strcpy?

s->name = name;
Since s->name is an array, you can't assign to it (it's not a modifiable lvalue) - it should be a compiler error. You'll have to strcpy or memcpy into it, but make sure name isn't too large.

setStudentID is perfectly fine, but setStudentName isn't. You're trying to assign a char* to an array, and that doesn't work. You'll have to use a function that copies it element-wise, like strcpy.

Related

Copy a string to a *struct member

I've looked through topics of similar kind, but didn't find a solution for my problem. I've got a struct like
typedef struct {
int number;
char *string;
} mystruct;
//Then define a pointer to the struct:
mystruct *data;
// Allocate memory:
data = malloc(sizeof(mystruct));
//Assign some number, let's say 5:
(*data).number = 5;
//So far ok, works (checked: 5 can be retrieved from the struct somewhere else) but:
strcpy((*data).string = "Hello!");
//This line fails with segmentation fault.
I don't understand why? Can somebody please explain what I'm doing wrong?
As mentioned in the comments, the problem with your attempted strcpy call is that your destination (the string member of your data) isn't a pointer to valid memory. (I am assuming that your invalid syntax, strcpy((*data).string = "Hello!") is a typo, and that your real code has something like strcpy((*data).string, "Hello!") – otherwise, it won't even compile, so you won't get a segmentation fault.)
There are several approaches to fix this, depending on what you actually want to happen. First, as also mentioned in the comments, you can just copy the pointer to the destination, like this1:
data->string = "Hello!";
However, this may not be what you want, because, if the source is not actually a literal, but a modifiable string, then, after that assignment, any changes to the source will also apply to your copy.
If you want a copy of the source data, you will need to allocate sufficient space for it, then call strcpy; like this:
#include <stdlib.h> // For malloc
const char* src = "Hello!";
data->string = malloc(strlen(src) + 1); // Add 1 for the required nul-terminator
strcpy(data->string, src);
However, the strdup function2 does the job of the above malloc and strcpy calls in one fell swoop:
data->string = strdup("Hello!");
In each of the above two cases, be sure to call free(data->string) when you're finished.
1 Note that you can use the "member access through pointer" (->) operator, rather than dereferencing and then using the . operator: data->number = 5 is equivalent to your (*data).number = 5.
2 Some compilers/platforms may not support the strdup function, but it is part of the ISO C Standard from C23
As other users mentioned, you need to allocate space for the string.
I would use a flexible array member and have only one allocation for the struct and string.
typedef struct {
int number;
char string[];
} mystruct;
mystruct *create(const char *str)
{
mystruct *ms = NULL;
if(str)
{
ms = malloc(sizeof(*ms) + strlen(str) + 1);
if(ms)
{
strcpy(ms -> string, str);
ms -> number = 0;
}
}
return ms;
}
int main(void)
{
mystruct *ms = create("Hello");
if(ms)
{
printf("ms -> string = `%s`\n", ms -> string);
}
free(ms);
}
I am not sure to understand this line
strcpy((*data).string = "Hello!");
Do you mean
strcpy((*data).string, "Hello!");
I believe the issue is that your struct contain a pointer char * string that doesn't point to a valid allocated memory.
You can try multiple solutions
Have struct with predefined size
typedef struct {
int number;
char string[100];
} mystruct;
You can allocate memory for the string during the initialization
data = malloc(sizeof(mystruct));
data->string = malloc(sizeof(char) *100));

Allocing memory to array of strings

So, i'm tring to allocate memory to insert file names in it. I have my struct Estado defined like this:
typedef struct estado{
char modo;
char jogador;
char matriz[8][8];
int pretas;
int brancas;
char *nome[10];
int current;
} Estado;
I tried doing this:
Estado insereFicheiro(Estado estado , char* nome){
estado.nome[estado.current] = malloc(sizeof(char*));
estado.nome[estado.current++] = nome;
return estado;
}
What am i doing wrong ?
There's two problems with the code you show:
With
estado.nome[estado.current] = malloc(sizeof(char*));
you allocate only space for a pointer, not the whole string. This is like you creating an array of one pointer. You need to allocate space for the string itself, whose length you get from strlen, and also for the null-terminator at the end:
estado.nome[estado.current] = malloc(strlen(nome) + 1); // +1 for null-terminator
With
estado.nome[estado.current++] = nome;
you overwrite the pointer you created above. This is equivalent to e.g. int a; a = 5; a = 10; and then be surprised that a is no longer equal to 5. You need to copy the string, not the pointer:
strcpy(estado.nome[estado.current++], nome);
Of course, you need to free the memory you allocate later in your code, once you're finished with it.
And of course you should have some bound-checking to make sure you don't go out of bounds of the estado.nome array (i.e. a check for estado.current < 10).

Converting a character pointer to uppercase in C

I have a pointer:
char * name;
it contains the string "test:case"
And I'm calling another function with it, and trying to store it in a structure. However, I want to capitalize the entire string first, but It doesn't seem to work.
void func(char * name) {
int i;
List * l;
l = malloc(sizeof(List));
for(i=0; i< strlen(name); i++) {
name[i] = toupper(name[i]);
}
l->name = name;
//CALL A FUNCTION TO LATER FREE ALLOCATED MEMORY
}
Where List is a struct that has a member (char *) named name.
This however, seg faults. I can't go about using non pointers in my case. As I have to use pointers and not character arrays, I'm trying to use toupper in every value of the char pointer, however this doesn't seem to work.
You're getting a segfault because the original string is presumably a literal, and it's not modifiable. You need to make a copy of it first.
void func(char * name) {
List * l;
l = malloc(sizeof(List));
name = strdup(name); // make a copy of name
for (char *p = name; *p; p++) {
*p = toupper(*p);
}
l->name = name;
}
Note that when you later free l, you'll first need to free l->name.
Since you only set the pointer l->name to name, this will crash the moment the original name is no longer there (maybe it was only on the stack?) and l->name is accessed. You need to malloc() space the size of strlen(name)+1, copy name there and set l->name to that address.
You have allocated the List, but not the name string itself.
You need to do:
l=malloc(sizeof(List));
l->name=(char*)malloc(strlen(name)+1); // You need to cater for the the final null char.
There are a few additional issues with your code, here is the correct one (didn't compile it, probably close to okay):
void func(char *name) {
List * l;
l = (List*)malloc(sizeof(List));
l->name = (char*)malloc(strlen(name)+1);
for(char *r=name, char *w=l->name; *r; ++r,++w) {
*w = toupper(*r);
}
*++w='\0';
}
And at every iteration, this code does not evaluate again and again strlen, which would be very bad.
There are two mistakes in the code.
char * name
The variable name contains a pointer to a string (i.e., an array of char).
When you write this:
name[i] = toupper(name[i]);
you are changing the original char itens of the string, if it is not a pointer to a constant string. If that is the case, it is a segmentation fault.
The other mistake is here:
l->name = name;
You are just assigning to the variable within the structure the pointer which was passed on through the variable name to the function. You should make a copy, like this:
strcpy(l->name, name);
This functions copies all the contents from the second argument to the first.
But that's not a good solution. If name contains a pointer to a constant string, it's still segmentation fault.
I'll rewrite your code:
void func(char * name) {
int i;
List * l;
l = malloc(sizeof(List));
char *buffer[strlen(name)]; //buffer of the contents pointed by *name* to upper case, initialized as empty string
for(i=0; i< strlen(name); i++) {
buffer[i] = toupper(name[i]);
}
buffer[i] = '\0'; //closing the string in i = strlen(name)
strcpy(l->name, buffer);
//CALL A FUNCTION TO LATER FREE ALLOCATED MEMORY
}
That way, you manipulate a copy of the original string, and than you make a copy of the buffer to the variable in the structure.
If you do this:
l->name = buffer;
you're only copying a local pointer, which will be gone with the end of the function.
I suggest you learn more about pointers, arrays and strings in C. In essence, a string is a array of char, with a '\0' in the final position. An empty string s has '\0' in s[0].
Edit: if you're used to languages which makes a copy of the string like this:
string1 = string2
you should always have in mind that, in C, that's pointer assignment. So, in C, that code would have to be written like this:
strcpy(string1, string2);
Hope that helps.

Uninitialized Structures c

i'm having trouble whit this code. I get the error " uninitialized local variable "question1" used", I'm new to c and I'm really trying to learn. I'm trying to build a quiz program where i can use both Structures and textfile. i'm just staring but the error gets in the way !!
the code is !
#pragma warning(disable:4996)
#include<stdio.h>
#include<stdlib.h>
#define max 70
struct question{
char *questions;
char *alter1;
char *alter2;
char *alter3;
char *alter4;
char correct;
};
int main(){
char *m;
struct question question1;
struct question Alt1;
question1.questions[max] = "what is my name?A:Haidar?B:Ali?C:Hagob?D:Aws?";
Alt1.alter1 = 'A';
Alt1.alter2 = 'B';
Alt1.alter3 = 'C';
Alt1.alter4 = 'D';
Alt1.correct;
m = question1.questions[max];
printf("%s\n", *m);
scanf("%c", &Alt1.correct);
if (Alt1.correct == Alt1.alter1);
{
print("right you have won\n");
}
if ((Alt1.correct) != Alt1.alter1); {
printf("sorry\n");
}
system("pause");
}
question1.questions is a char pointer. When you write question1.questions[max] you are saying "take the current value of the pointer, add (50 times the length of one char) to it, and dereference the result.
But question1.questions was never given an initial value. It is an uninitialized pointer, hence it doesn't point anywhere. You can't, therefore, dereference it.
Since you say it is supposed to be an array of strings, not a single string, your structure definition should look like:
#define max 70
struct question{
char *questions[max];
char *alter1;
char *alter2;
char *alter3;
char *alter4;
char correct;
};
Then questions[0] would refer to the FIRST string. questions[max-1] would be the LAST string. questions[max] would be an error, past the end of the array.

Assigning a string to a pointer in a struct

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Person {
char *forename;
char *surname;
int age;
};
void change_struct(struct Person *person, char *forename, char *surname,
int age);
void print_struct(struct Person *person);
int main(void)
{
struct Person person1;
person1.forename = malloc((strlen("Max") + 1) * sizeof(char));
if (!person1.forename) {
exit(EXIT_FAILURE);
}
strcpy(person1.forename, "Max");
person1.surname = malloc((strlen("Mustermann") + 1) * sizeof(char));
if (!person1.surname) {
exit(EXIT_FAILURE);
}
strcpy(person1.surname, "Mustermann");
person1.age = 35;
print_struct(&person1);
change_struct(&person1, "Hans", "Bauer", 45);
print_struct(&person1);
free(person1.forename);
free(person1.surname);
exit(EXIT_SUCCESS);
}
void change_struct(struct Person *person, char *forename, char *surname,
int age)
{
person->forename = realloc(person->forename,
(strlen(forename) + 1) * sizeof(char));
if (!person->forename) {
exit(EXIT_FAILURE);
}
strcpy(person->forename, forename);
person->surname = realloc(person->surname,
(strlen(surname) + 1) * sizeof(char));
if (!person->surname) {
exit(EXIT_FAILURE);
}
strcpy(person->surname, surname);
person->age = age;
}
void print_struct(struct Person *person)
{
printf("%s\n", person->forename);
printf("%s\n", person->surname);
printf("%d\n", person->age);
}
When assigning a string to a pointer in a struct is it well-defined behaviour if I would do
person1.forename = "Max";
person1.surname = "Mustermann";
in main() initially instead of using malloc() and strcpy()?
NOTE: (Of course in this specific case I would need to also change the realloc() calls in change_struct() since it is undefined behaviour when realloc() receives a non- malloc(), calloc(), or realloc() created pointer.)
If dynamic memory allocation should be required could you give an explanation why?
As long as you don't want to modify the contents,
person1.forename = "Max";
person1.surname = "Mustermann";
are perfectly valid.
In this case, person1.forename will be a pointer to the string literal and any attempt to modify the contents will result in undefined behaviour.
That said,
for print_struct() function, you don't need to pass a pointer to the structure.
sizeof(char) is guaranteed to produce 1 in C. Using it for multiplication (to get the size in malloc())is redundant, can be omitted easily.
person1.forename = "Max";
person1.surname = "Mustermann";
You are making your pointers point to string literals , so make a note that string literls are read-only. So once you have the above initialization you can't modify the string the pointer is pointing to which is not the case with the memory allocated by malloc() and family functions.
Since you are allocating memory explicitly you can use it to read and write.So you need dynamic memory allocation here if you wish to modify the string the pointers are pointing to
"Max" is a null-terminated read-only string literal in C; using a const char* to point to the first character is well-defined and idiomatic.
You ought to change the types of your structure elements to const char* if you are going to populate it in such a manner as the behaviour on amending a read-only literal is undefined.
Of course, you can set forename &c. to point to another character string.
It is a bad approach. In this case you may not apply function realloc inside function change_struct because string literals have static storage duration. They are not allocated dynamically
This function is a general-purpose function and it can accept arguments for parameters forename and surname of various kind not only string literals.
For example the function can be called like
char name[] = "Hans";
change_struct(&person1, name, "Bauer", 45);
Or even like
if ( some_condition )
{
char name[] = "Hans";
change_struct(&person1, name, "Bauer", 45);
}
In this case any changing of name will also change the string pointed to by data member forename of an object of the type of the structure. Moreover if array name will be defined in some local scope as it is shown in the example with the if statement then the pointer will be even invalid.

Resources