[英]Pointers to char* inside a struct in C
我有一個可能很愚蠢的問題...但是我定義了一個內部帶有一些char *的結構。 現在,當我嘗試更改這些字符的值時。 編譯時它不會出現問題,但是當我執行它時,程序會停止。
這是我用來檢查問題出在哪里的測試功能:
struct myret {
int age;
char *name;
char *affiliation_number;
};
void obtain_name_affiliation_number(struct myret *r)
{
int age;
char *name;
char *affiliation_number;
FILE *user_data;
user_data = fopen("user_data.txt", "r");
fscanf(user_data, "%d", &age);
fscanf(user_data, "%s", &name);
fscanf(user_data, "%s", &affiliation_number);
fclose(user_data);
r->age = age;
r->name = name;
r->affiliation_number = affiliation_number;
return 0;
}
int main(void)
{
struct myret r;
int rc = obtain_name_affiliation_number(&r);
if (rc == 0) {
printf("%d %s %s\n", r.age, r.name, r.affiliation_number);
}
getchar();
return 0;
}
謝謝一切=)
您無需使用&
傳遞char *
的地址,而刪除那些&
:
fscanf(user_data, "%s", &name);
^
fscanf(user_data, "%s", &affiliation_number);
^
另外,在通過malloc
使用name
和affiliation_number
之前分配內存,例如:
char *name = malloc(100 * sizeof(char));
僅聲明一個char *
不會為其分配任何內存。 您要么需要指定一個靜態大小(例如char name[100]
),要么需要動態分配內存(例如通過使用malloc
)。 如果完成后使用malloc
,請記住要free
內存。
另一個問題是在fscanf
為char *
使用&
。 格式字符串后面的scanf
和fscanf
的變量列表是放置輸入值的指針的列表。 當我們使用int age
類的類型int age
我們需要傳遞&age
,因為在age
之前, &
使其成為指向age
的指針,即int *
。 但是char *name
已經是一個指針,因此您不需要&
在這里。 通過使用&name
您將創建一個在此處不需要的指針或char **
指針。
您僅為name和affiliation_number的緩沖區地址分配了空間,但從未為這些地址指向緩沖區分配緩沖區。
因此,當您在fscanf()
使用它們時,會出現問題。 編譯器會通過以下注釋警告您(無論如何為gcc),這些指針是錯誤的類型-它們應該是您要fscanf
覆蓋的目標緩沖區中第一個字節的地址:
foo.c:19:5: warning: format ‘%s’ expects argument of type ‘char *’, but argument 3 has type ‘char **’ [-Wformat]
foo.c:20:5: warning: format ‘%s’ expects argument of type ‘char *’, but argument 3 has type ‘char **’ [-Wformat]
相反,有些事情要做-如果您的系統不支持fscanf的"%ms"
規范,請嘗試以下操作:
char buffer[1024]
以將數據讀入其中。 buffer
或&buffer[0]
(對於某些純粹主義者)用作字符串fscanfs中的目標 %1023s
之類的方法防止讀取的數據超過緩沖區的長度-溢出會導致程序瘋狂。 strdup
將數據克隆為name
或affiliation_number
。 這將malloc()
一個新的內存塊,其大小適合您讀取的字符串並將數據復制到其中。 無論您使用這些步驟還是使用"%ms"
方法,緩沖區都需要稍后進行free()
編輯,以避免內存泄漏!
這有點簡單(特別是限制為1024),但是應該讓您正確地開始。
例:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
/* tested with user_data.txt containing "12 Barney 42" or "12 Barney" */
struct myret {
int age;
char *name;
char *affiliation_number;
};
int obtain_name_affiliation_number(struct myret *r)
{
int success = 0;
int age;
char *name = 0;
char *affiliation_number = 0;
FILE *user_data = fopen("user_data.txt", "r");
if(user_data)
{
#if 0 /* use if you have "%ms" */
if((1 == fscanf(user_data, "%d", &age)) &&
(1 == fscanf(user_data, "%ms", &name)) &&
(1 == fscanf(user_data, "%ms", &affiliation_number)))
{
success = 1;
} else {
/* a small annoyance: if only the first "%ms" succeeded,
* we need to free it:
*/
if(name)
free(name);
}
#else
char buffer[1024];
/* This if-structure can be used with the "%ms" as well, and
* would make the "annoyance" look a lot cleaner
*/
if(1 == fscanf(user_data, "%d", &age))
{
if(1 == fscanf(user_data, "%1023s", buffer))
{
name = strdup(buffer);
if(1 == fscanf(user_data, "%1023s", buffer))
{
affiliation_number = strdup(buffer);
success = 1;
}
}
}
#endif
fclose(user_data);
} else perror("error opening data file");
if(success)
{
r->age = age;
r->name = name;
r->affiliation_number = affiliation_number;
}
return success;
}
int main(void)
{
struct myret r;
int rc = obtain_name_affiliation_number(&r);
if(rc) {
printf("%d %s %s\n", r.age, r.name, r.affiliation_number);
free(r.name);
free(r.affiliation_number);
}
else
fputs("an error occurred reading data\n", stderr);
getchar();
return 0;
}
還有其他方法。 name
和affiliation_number
可以在結構中聲明為char name[512];
例如,但是幾乎不可能事先選擇這個數字,並且希望正確無誤。 相反,這很常見:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct myret *myret_alloc(int age, char *name, char *affiliation_number)
{
struct myret *r = 0;
if(r = (struct myret*)calloc(1, sizeof(struct myret))) {
r->age = age;
r->name = name; /* or use r->name = strdup(name); */
r->affiliation_number = affiliation_number;
/* note that using strdup would mean the calling function should
* do its own cleanup of its own name and affiliation_number vars
*/
}
return r;
}
void myret_free(struct myret *r)
{
/* this can be called on partially-allocated myret objects */
if(r->affiliation_number)
free(r->affiliation_number);
if(r->name)
free(r->name);
free(r);
}
然后,其他兩個函數變為(假設fscanf
可以"%ms"
):
struct myret *obtain_name_affiliation_number(void)
{
struct myret *r = (struct myret*)0;
FILE *user_data = fopen("user_data.txt", "r");
if(user_data)
{
int age;
char *name = 0; /* the 0 allows us to see if it was used later */
char *affiliation_number = 0;
if((1 == fscanf(user_data, "%d", &age)) &&
(1 == fscanf(user_data, "%ms", &name)) &&
(1 == fscanf(user_data, "%ms", &affiliation_number)))
{
/* The name and affiliation_number were malloc()ed by "%ms"
* so there's nothing to clean up in this function, and
* we can let myret_free() just free those memory areas.
* This also means myret_alloc doesn't need strdup().
*/
r = myret_alloc(age, name, affiliation_number);
} else {
if(name) /* clean up name if it got allocated */
free(name);
}
fclose(user_data);
} else perror("error opening data file");
return r;
}
int main(void)
{
struct myret *r = obtain_name_affiliation_number();
if(r) {
printf("%d %s %s\n", r->age, r->name, r->affiliation_number);
myret_free(r);
}
else
fputs("an error occurred reading data\n", stderr);
getchar();
return 0;
}
您也可以一次將fscanf
用於所有這三個:
if(3 == fscanf(user_data, "%d %ms %ms", &age, &name, &affiliation_number))
...
祝好運!
首先,你需要malloc
內存為您的數據,否則將無處可存儲它:
char *affiliation_number = (char*) malloc(STRING_LENGTH * sizeof (char));
如果malloc
失敗,它將返回NULL
,您必須檢查以下錯誤情況:
if (!affiliation_number) {
// report error
}
然后, affiliation_number
已經是一個指針,您不需要&
在:
fscanf(user_data, "%s", affiliation_number);
name
相同。
首先,您必須使用malloc();
和free();
當您使用char* a="always use malloc";
編譯器將其用作const char * a
char* a;
分配內存char* a;
當a將用作變量字符串時 free();
之后避免內存泄漏
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.