c语言之sizeof,static局部变量学习

本文详细介绍了C语言中的sizeof运算符,强调它是一个编译时运算符,不涉及运行时计算。接着讨论了static变量的作用,指出static会改变局部变量的生命周期,使其在函数多次调用中保持值的持久。最后,通过一个年月日转换函数的例子,展示了如何利用static和const优化内存使用和程序安全性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

sizeof关键字

在c语言中,sizeof并不是一个函数,而是一个单目运算符,如同c语言中的++,-- 一般。这一点我们一定要注意。sizeof的用法并不单一,sizeof(类型),这样的语法是可行的,或者直接是sizeof(4),或者sizeof(a)。
为了深刻理解sizeof是一个单目运算符号,看这样一段代码:

#include<iostream>
using namespace std;

int main(){
  int a = 1;
  int len_1 = sizeof(a);
  int len_2 = sizeof(int);
  int len_3 = sizeof a;
  int len_4 = sizeof(4);
  cout<<"len_1 = "<<len_1<<" "<<"len_2 = "<<len_2<<" "<<"len_3 = "<<len_3<<" "<<"len_4 = "<<len_4<<endl;
}

正是因为sizeof是一个单目运算符号,所以len_3定义也是成立的。
同样我们需要注意:sizeof只是一个运算符,在编译中就会把具体的值进行返回,所以在运行时候是并不会计算的,为了更加深刻理解这个代码,我们看这样一个例子。

#include<iostream>
using namespace std;
int main(){
	int a = 2;
	int b = sizeof(++a);
	cout<<"a = "<<a<<" "<<"b = "<<b<<endl;
	return 0;
}

这段代码的输出应该是a = 2 b = 3。那么很容易产生这样的疑问,a的值为什么不是3呢,代码中明明把a自加了一次。这也就是我们强调的原因,sizeof在编译过程中,就把sizeof(a++)之类的代码直接便以为sizeof(int)这样。执行的代码当然和a没有关系,所以a的值并不会产生变化。

static变量

前面我们提到过,被static修饰的静态变量会放于data区,那么static的作用到底是什么呢?

static会改变数据的生存周期。我们举这样一个例子:

#incldue<iostream>
using namespace std;

int Func(){
	static int c = 0;
	++c;
	return c;
}

int main(){
	int c = Func();
	int d = Func();
	cout<<"c = "<<c<<" "<<"d = "<<d<<endl;
	return 0;
}

被static修饰的局部变量并不会放入stack区,而是放入了data区。并且只会有一次初始化,当程序再一次调用这个函数的时候,static变量并不会经过第二次初始化,而是直接跳过初始化代码执行。所以上面代码执行的结果应该是c = 1,d = 2。
下面看这样一段十分具有迷惑性的代码(c99标准下可以通过,c11不可以)

#include<iostream>
using namespace std;

void func(int x){
  static int a = 0;
  static int b = x;
  ++a;
  ++b;
  cout<<"a = "<<a<<" "<< "b = "<<b<<endl;
}

int main(){
  for (int i = 10; i > 0; --i){
    func(1);
  }
  return 0;
}

代码运行图:
在这里插入图片描述
当调用函数func时候,系统会给func分配相应的栈帧空间,但是由于func定义的两个变量均为static变量,所以并不会存放到栈帧当中,而是在data区进行了初始化。所以当函数生命周期结束,栈帧被释放的时候,并不会影响这两个static的值的生命周期。当下一次调用函数时候,也不会进一步的初始化,而是跳过初始化代码。那么究竟如何实现这个原理呢?
下面我们看这样一段代码:

#include<stdio.h>
int main()
{
	int initNum = 3;
	for (int i=5; i > 0; --i)
	{
		static int n1 = initNum;
		//我们在这里增加了两句代码,把n1所指的内存地址后面4个字节赋值成0
		int* p = &n1;
		p++;
		*p = 0;
		//end
		n1++;
		printf("%d\n", n1);
		}
	getchar();
	return 0;
}

这里我们只是举个例子,因为每个编译器的编辑地点都是不同的,当我们经过一次初始化以后,对于编译器而言,会在data区这个static变量附近的区域做一个标记,用来标记当前变量已经经过一次初始化,并且不会经历第二次初始化。实际上我们可以这样说,不管是什么样的变量,在他的生命周期当中,他都只能经历一次初始化,有人会问那为什么stack区域变量可以一直初始化,这里我们一定要明白,当经历过一次函数调用以后,里面的变量已经结束了其生命周期被释放,第二次调用生成的是新的变量,尽管变量名是相同的,这一点我们一定要注意。

对之前程序的升级

学完了static局部变量,我们回头再看我们之前写过的年月日函数,我们不经的提出了这样的问题,当时我们定义的YearMonthDay_to_total()函数,会循环调用YearMonth_to_day()这个函数,所以这个函数中的数组也会被不停的初始化,这样的做法其实是十分不合理的,对内存开销也十分不好,所以我们应该把days数组定义为static变量。再还有,既然我们对于这个数组的操作仅限于访问其中的内部数据,那么就应该把它定义为const类型的常变量,这样同样增加了程序数据的安全性。
最后我们思考最后一个这样的问题,既然我们可以用列表法加载月份,那么我们是否也可以用同样的方法在程序开始前,就把每个月最后一天是当年第几天这个列表加载出来呢?这样同样可以增加代码的可读性等。

我们看一下升级以后的代码:

#include<stdio.h>
#include<stdbool.h>

bool Is_LeapYear(int year){
  return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
}

int YearMonth_to_day(int year, int month){
  const static int days[13] = {29, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
  if (Is_LeapYear(year) && 2 == month){
    month = 0;
  }
  return days[month];
}

int YearMonthDay_to_total(int year, int month, int day){
  const static int totals[14] = {0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};
  if(month > 2 && Is_LeapYear(year)){
  	return totals[month] + day + 1;
  }else{
  	return totals[month] + day;
  }
}

bool judge(int year, int  month, int day){ 
  if(year <= 0){
    printf("请输入公元后的年份 \n");
    return false;
  }
  if (month <=0 || month >= 13){
    printf("请输入正确的年份 \n");
    return false;
  }
  if (!(day <= YearMonth_to_day(year, month))){
    printf("没有这个日期 \n");
    return false;
  }
  return true;
  
}

void operator(){
  int year, month, day; 
  bool tag = true;
  while(1){
    printf("请输入年月日 \n");
    //char c = getchar();
    char c;
    scanf("%d %d %d",&year, &month, &day);
    tag = judge(year, month, day);
    if (tag){
      
      if(Is_LeapYear(year)){
        printf("润年 \n");
      }else{
        printf("非润年 \n");
      }
      printf("这个月有%d天\n",YearMonth_to_day(year, month));
      printf("这是这一年的第%d天 \n",YearMonthDay_to_total(year, month, day));
    }
    printf("是否继续Y/N \n");
    c = getchar();
    scanf("%c",&c);
    if(!('y' == c || 'Y' == c)){
      break;
    }
  }
}

int main(){
  operator();
  return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值