Wetts's blog

Stay Hungry, Stay Foolish.

0%

static 存储类指示编译器在程序的生命周期内保持局部变量的存在,而不需要在每次它进入和离开作用域时进行创建和销毁。因此,使用 static 修饰局部变量可以在函数调用之间保持局部变量的值。

static 修饰符也可以应用于全局变量。当 static 修饰全局变量时,会使变量的作用域限制在声明它的文件内。

static 是全局变量的默认存储类,以下两个变量 (count 和 road) 都有一个 static 存储类。

1
2
3
4
5
6
7
8
static int Count;
int Road;

main()
{
printf("%d\n", Count);
printf("%d\n", Road);
}

register 存储类用于定义存储在寄存器中而不是 RAM 中的局部变量。这意味着变量的最大尺寸等于寄存器的大小(通常是一个词),且不能对它应用一元的 ‘&’ 运算符(因为它没有内存位置)。

1
2
3
{
register int miles;
}

寄存器只用于需要快速访问的变量,比如计数器。还应注意的是,定义 ‘register’ 并不意味着变量将被存储在寄存器中,它意味着变量可能存储在寄存器中,这取决于硬件和实现的限制。

auto 存储类是所有局部变量默认的存储类。

1
2
3
4
{
int mount;
auto int month;
}

上面的实例定义了两个带有相同存储类的变量,auto 只能用在函数内,即 auto 只能修饰局部变量。

参考:http://www.cnblogs.com/xiaorenwu702/p/5739845.html

用C语言编写程序的时候,我们经常会遇到这样一种情况:希望在头文件中定义一个全局变量,然后包含到两个不同的c文件中,希望这个全局变量能在两个文件中共用。

举例说明

项目文件夹project下有main.c、common.c和common.h三个文件,其中common.h文件分别#include在main.c和common.c文件中。现在希望声明一个字符型变量key,在main.c和common.c中公用。

有人想,既然是想两个文件都用,那就在common.h中声明一个unsigned char key,然后由于包含关系,在main.c和common.c中都是可见的,所以就能共用了。

问题

这种想法其实是很多初学者都会想到的,想起来确实有道理,但是实际写出来,我们发现编译的时候编译器提示出错,一般提示大概都类似于:Error: L6200E: Symbol key multiply defined (by common.o and main.o). 也就是说编译器认为我们重复定义了key这个变量。这是因为#include命令就是原封不同的把头文件中的内容搬到#include的位置,所以相当于main.c和common.c中都执行了一次unsigned char key,而C语言中全局变量是项目内(或者叫工程内)可见的,这样就造成了一个项目中两个变量key,编译器就认为是重复定义。

解决办法

使用extern关键字来声明变量为外部变量。具体说就是在其中一个c文件中定义一个全局变量key,然后在另一个要使用key这个变量的c文件中使用extern关键字声明一次,说明这个变量为外部变量,是在其他的c文件中定义的全局变量。请注意我这里的用词:定义和声明。例如在main.c文件中定义变量key,在common.c文件中声明key变量为外部变量,这样这两个文件中就能共享这个变量key了。

main.c

1
2
#include "common.h"
unsigned char key;

common.c

1
2
#include "common.h"
extern unsigned char key;

路径:/Users/tonyvicky/Library/Developer/Xcode/DerivedData

修改路径:File -> Project Settings

转自:https://blog.csdn.net/zhaoshuzhaoshu/article/details/37600857/

什么是大端,什么是小端

  • 所谓的大端模式,是指数据的低位保存在内存的高地址中,而数据的高位保存在内存的低地址中;
  • 所谓的小端模式,是指数据的低位保存在内存的低地址中,而数据的高位保存在内存的高地址中。

为什么会有大小端

为什么会有大小端模式之分呢?这是因为在计算机系统中,我们是以字节为单位的,每个地址单元都对应着一个字节,一个字节为 8bit。但是在 C 语言中除了 8bit 的 char 之外,还有 16bit 的 short 型,32bit 的 long 型(要看具体的编译器),另外,对于位数大于 8 位的处理器,例如 16 位或者 32 位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如果将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式。例如一个 16bit 的 short 型 x,在内存中的地址为 0x0010,x 的值为 0x1122,那么 0x11 为高字节,0x22 为低字节。对于大端模式,就将 0x11 放在低地址中,即 0x0010 中,0x22 放在高地址中,即 0x0011 中。小端模式,刚好相反。我们常用的 X86 结构是小端模式,而 KEIL C51 则为大端模式。很多的 ARM、DSP 都为小端模式。有些 ARM 处理器还可以由硬件来选择是大端模式还是小端模式。

大小端在内存中的存放方式举例:

例如,16bit 宽的数 0x1234 在 Little-endian 模式 CPU 内存中的存放方式(假设从地址 0x4000 开始存放)为:

内存地址 0x4000 0x4001
存放内容 0x34 0x12

而在 Big-endian 模式 CPU 内存中的存放方式则为:

内存地址 0x4000 0x4001
存放内容 0x12 0x34

32bit 宽的数 0x12345678 在 Little-endian 模式 CPU 内存中的存放方式(假设从地址 0x4000 开始存放)为:

内存地址 0x4000 0x4001 0x4002 0x4003
存放内容 0x78 0x56 0x34 0x12

而在 Big-endian 模式 CPU 内存中的存放方式则为:

内存地址 0x4000 0x4001 0x4002 0x4003
存放内容 0x12 0x34 0x56 0x78

如何测试编译器是大端还是小端:

下面这段代码可以用来测试一下你的编译器是大端模式还是小端模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include<stdio.h>

int main()

{

    short int x;

    char x0,x1;

    x=0x1122;

    x0=((char *)&x)[0];  //低地址单元

    x1=((char *)&x)[1];  //高地址单元

    printf("x0=0x%x,x1=0x%x",x0,x1);// 若x0=0x11,则是大端; 若x0=0x22,则是小端......

    return 0;
}

  • bufio.Reader/Writer 分别实现了io.ByteReader和io.ByteWriter
  • bytes.Buffer 同时实现了io.ByteReader和io.ByteWriter
  • bytes.Reader 实现了io.ByteReader
  • strings.Reader 实现了io.ByteReader

  • os.File 同时实现了io.Reader和io.Writer
  • strings.Reader 实现了io.Reader
  • bufio.Reader/Writer 分别实现了io.Reader和io.Writer
  • bytes.Buffer 同时实现了io.Reader和io.Writer
  • bytes.Reader 实现了io.Reader
  • compress/gzip.Reader/Writer 分别实现了io.Reader和io.Writer
  • crypto/cipher.StreamReader/StreamWriter 分别实现了io.Reader和io.Writer
  • crypto/tls.Conn 同时实现了io.Reader和io.Writer
  • encoding/csv.Reader/Writer 分别实现了io.Reader和io.Writer
  • mime/multipart.Part 实现了io.Reader

除此之外,io包本身也有这两个接口的实现类型。如:

  • 实现了Reader的类型:LimitedReader、PipeReader、SectionReader
  • 实现了Writer的类型:PipeWriter

以上类型中,常用的类型有:os.File、strings.Reader、bufio.Reader/Writer、bytes.Buffer、bytes.Reader

假设T是struct,那么Go里面遵循下面几个原则:

  1. T的方法集仅拥有 T Receiver 方法。
  2. *T 方法集则包含全部方法 (T + *T)
  3. 如果结构的实例x是“可被寻址的”,且&x的方法集中包含方法m,则x.m()为(&x).m()的速记(快捷方式)

转自:http://studygolang.com/articles/3496

概述

Go 语言中的 newmake 一直是新手比较容易混淆的东西,咋一看很相似。不过解释两者之间的不同也非常容易。 他们所做的事情,和应用的类型也不相同。
二者都是用来分配空间。

new 函数

  • new 是内建函数,函数原型为
    1
    func new(Type) *Type
    官方文档描述为:

    The new build-in function allocates memory(仅仅分配空间). The first argument is a type, not a value, and the value returned is a pointer to a newly allocated zero value of that type.

翻译如下:

内置函数 new 分配空间。传递给new 函数的是一个类型,不是一个值。返回值是 指向这个新分配的零值的指针。
根据这段描述,我们也可以自己实现一个类似 new 的功能

1
2
3
4
5
func newInt *int {
var i int
return &i //为何可以返回局部变量呢?
}
someInt := newInt()

这里要注意的第一点是,返回值是一个指针。
然后,为何一个golang 中的函数可以返回局部变量呢?

make 函数

make 也是内建函数,你可以从 http://golang.org/pkg/builtin/#make 看到它, 它的函数原型 比 new 多了一个(长度)参数,返回值也不同。

函数原型是:

1
func make(Type, size IntegerType) Type
  • 第一个参数是一个类型,第二个参数是长度
  • 返回值是一个类型

官方描述为:

The make built-in function allocates and initializes an object(分配空间 + 初始化) of type slice, map or chan**(only)**. Like new , the first arguement is a type, not a value. Unlike new, make’s return type is the same as the type of its argument, not a pointer to it. The specification of the result depends on the type.

翻译为:

内建函数 make 分配并且初始化 一个 slice, 或者 map 或者 chan 对象。 并且只能是这三种对象。 和 new 一样,第一个参数是 类型,不是一个值。 但是make 的返回值就是这个类型(即使一个引用类型),而不是指针。 具体的返回值,依赖具体传入的类型。

Slice : 第二个参数 size 指定了它的长度,此时它的容量和长度相同。

你可以传入第三个参数 来指定不同的容量值,但是必须不能比长度值小。

比如: make([]int, 0, 10)

Map: 根据size 大小来初始化分配内存,不过分配后的 map 长度为0。 如果 size 被忽略了,那么会在初始化分配内存的时候 分配一个小尺寸的内存。

Channel: 管道缓冲区依据缓冲区容量被初始化。如果容量为 0 或者被 忽略,管道是没有缓冲区的。

1
2
var p *[]int = new([]int)
var v []int = make([]int, 10)

上述第一条语句 使用 new() 函数为 切片结构分配内存,*p == nil (这意味着什么? 意味着没有对Slice结构进行初始化), 但是在实际中这种用法很少使用。

第二条语句使用 make() 函数创建了一个有10个元素的 Slice对象。

总结

new 的作用是 初始化 一个指向类型的指针 (*T), make 的作用是为 slice, map 或者 channel 初始化,并且返回引用 T

make(T, args)函数的目的与new(T)不同。它仅仅用于创建 Slice, Map 和 Channel,并且返回类型是 T(不是T*)的一个初始化的(不是零值)的实例。 这中差别的出现是由于这三种类型实质上是对在使用前必须进行初始化的数据结构的引用。 例如, Slice是一个 具有三项内容的描述符,包括 指向数据(在一个数组内部)的指针,长度以及容量。在这三项内容被初始化之前,Slice的值为nil。对于Slice,Map和Channel, make()函数初始化了其内部的数据结构,并且准备了将要使用的值。

问题:栈中分配的变量,出栈的时候不会被释放吗?

我们注意到有如下函数:

1
2
3
4
5
func newInt *int {
var i int
return &i //为何可以返回局部变量呢?
}
someInt := newInt()

有个回答回答的很好: golang 和 c 语言不一样,栈区分配的存储空间不会随着函数的返回而释放,本地变量地址所占据的存储空间会生存下来。