%d 是打印整形的, La 是 你定义的结构体类型,当然不能正确输出了
SqList指针la没有初始化,需要主程序初始化下。
SqList *La = (SqlList*)malloc(sizeof(SqList));
另外,建议顺序表里,malloc的时候,乘以Book的最大数量,否则你初始化了一个寂寞
既然知道了如何解析格式符,那么我们似乎就可以实现printf函数了?可是等一下,我们在写printf的时候为什么可以写多个参数?
一般而言,如果我们仅仅跟着学校学习的那点C语言知识,肯定不会讲什么变参函数的,但是printf这个函数的存在却离不开变参函数这个概念。而且,配合变参函数,你还可以实现一些类似于其它更加高级语言的重载、默认参数等功能。
如果需要实现变参函数,那么有一个库文件就很关键了,那就是stdarg.h
。你首先需要包含这个库,然后按照规定的写法就可以实现变参函数了。例如下面我就写了个简单的变参整数加法函数:
#include <stdarg.h>
int MySum(int count, ...)
{
va_list num; // 一个宏定义类型,通过它我们可以访问参数数据
int sum = 0;
va_start(num, count); // 确定起始参数
for(int i = 0; i < count; i++)
{
sum += va_arg(num, int); // 从起始参数开始取一个int类型的数据并加到sum中,同时num将会指向下一个参数
}
va_end(num); // 释放
return sum;
}
通过这个函数我们即可以实现一个变参的加法。我们来看一下这里的写法,其实很简单,除了格式要求在函数的形参列表末尾加上三个点表示这是个变参函数外,重点也就一个数据类型和三个函数。
不过为了防止看不懂,我们来从原理上理解一下这个功能。(此部分仅作参考,我并没有实际去看编译器的内容,而源文件也被隐藏起来了,故下面的内容大多是基于我自己的分析)。
首先是三个点的作用,很显然这个东西是给编译器看的,因为如果不加三个点,那么我们使用函数的时候,实参与形参个数对不上,编译器就会报错,而这三个点就可以告诉编译器,这是个变参函数,实参多了少了你别叫唤。
然后是一个数据类型即va_list
,这个类型从某种意义上感觉应该是个空指针,这个指针可以用来在栈中取数据。当然它也可能是个结构体,但至少绝对拥有指针的功能。
而剩下三个函数中,第一个调用的是va_start
,它应该是用来对va_list
这个指针赋初值。这里我认为应该是程序在编译运行后,函数的实参应该是放在一个栈里的,并且是有规律有顺序的存放的,那么我们要取一个数据,只要确定了这个数据的首地址(va_list指向的位置
)和长度(数据类型),就可以把数据正确取出来了,取出来后因为数据被整齐地排列着,所以指针被完美地跳转到下一个实参的首地址。而va_start
其实作用就是确定这个实参列表整体的首地址。
然后是第二个函数,在程序中被反复调用的va_arg
函数,显然通过我们刚才的分析,应该不难知道这个函数其实就是用来取数据同时移动指针的,在这个函数的参数中,除了要取数据的va_list
,还包括了要取的数据类型(虽然不知道为什么这个数据类型可以直接被写成类型的名称形式,应该是宏定义的一种用法),然后通过这个函数,我们就可以按照格式取出数据,同时把va_list
跳转到下一个参数的位置。
最后第三个函数,我们可以调用va_end
把指针指向一个空地址,防止野指针对内存造成破坏。
除此之外,其实stdarg中还有很多其它的函数,不过我们平常不大用得到,如果感兴趣的话可以自行研究研究。
现在,我们来试试刚刚写的程序吧,在主函数中如此调用:
int result = MySum(5, 1, 2, 3, 4, 5);
printf("%d\n", result);
最后我们可以得到一个15
的输出。
针对该问题,根据参考资料中的描述,printf函数中的占位符是在函数内部解析的,因此首先需要检查printf函数调用是否正确,参数是否正确传递。其次,还需要检查程序中是否有可能影响printf函数执行的其他错误,如死循环等。最后,如果以上都没有问题,可以考虑在相应的位置打上调试语句,输出相关的变量以排查问题。
示例代码:
#include <stdio.h>
#include <stdlib.h>
struct node {
int data;
struct node *next;
};
void printList(struct node *head) {
while (head != NULL) {
printf("%d ", head->data);
head = head->next;
}
}
int main() {
struct node *p, *head;
int i, n, x;
printf("input n: ");
scanf("%d", &n);
head = NULL;
for (i = 0; i < n; i++) {
printf("input a number: ");
scanf("%d", &x);
p = (struct node *) malloc(sizeof(struct node));
p->data = x;
p->next = NULL;
if (head == NULL) head = p;
else {
p->next = head;
head = p;
}
}
printf("The list La: ");
printList(head);
return 0;
}
其中,printList函数用于输出链表中的元素,如果该函数未被执行,则说明问题可能出现在函数调用处。可以在printf函数之前加上一个调试语句,如printf("printList is called\n"),检查输出结果是否符合预期。
如果函数调用正确,可以考虑检查程序中是否存在死循环等问题,在本例中,需要检查数据的输入是否正确,循环是否正确执行等。
如果以上两个方面都没有问题,可以在printf函数前后都打上调试语句,输出相关的变量,以便进一步排查问题。
完整的代码如下:
#include <stdio.h>
#include <stdlib.h>
struct node {
int data;
struct node *next;
};
void printList(struct node *head) {
while (head != NULL) {
printf("%d ", head->data);
head = head->next;
}
}
int main() {
struct node *p, *head;
int i, n, x;
printf("input n: ");
scanf("%d", &n);
head = NULL;
for (i = 0; i < n; i++) {
printf("input a number: ");
scanf("%d", &x);
p = (struct node *) malloc(sizeof(struct node));
p->data = x;
p->next = NULL;
if (head == NULL) head = p;
else {
p->next = head;
head = p;
}
}
printf("printList is called\n");
printf("The list La: ");
printList(head);
printf("printList is done\n");
return 0;
}