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"
仕様をサポートしていない場合は、これを試してください:
- acquire_name_affiliation で、
char buffer[1024]
データを読み込む a を作成します。
- 文字列 fscanfs のターゲットとして
buffer
or (特定の純粋主義者向け) を使用します。&buffer[0]
- などを使用
%1023s
して、データの読み取りがバッファの長さを超えないようにします。オーバーランはプログラムを狂わせます。
- fscanf が成功を返した場合 (期待される 1 つのフィールドについて、fscanf は値 1 を返す必要があります。そうでない場合、入力が正しくない可能性があります)、 を使用してデータをまたは
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
andは、たとえば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
次に、他の 2 つの関数は次のようになります ( canと仮定"%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
3 つすべてを一度に使用することもできます。
if(3 == fscanf(user_data, "%d %ms %ms", &age, &name, &affiliation_number))
...
幸運を!