0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

[CS50] week 1 - C言語

Last updated at Posted at 2023-01-22

初めに

今回はedXのコースに参加してC言語基礎の一部をまとめていきたいと思います。

week 1

ライブラリ:

Basic

#include <stdio.h>
int main(void)
{
  printf("hello, world\n");
  // return 0;
}
// hello, world
  • #include:プログラムに必要なヘッダーファイルを読み込むのを、コンパイラに明示する。

  • <>:ファイルのパスをsystemに指定する。

  • stdio.hstandard input(stdin) / standard output(stdout)を合わせてstdio、標準入力と出力を扱うヘッダーファイル。

  • stdint.hstandard integer、整数型の集合。

  • math.h:数学関数。

  • stdlib.hstandard library、標準ライブラリ関数。
    (...ヘッダーファイルがありすぎてここのは一部だけ)

  • int:整数型。(整数値を返す)

  • main:関数名。

  • void:引数がないことを明示する。(デフォルト設定として)

  • return:関数の返り値。(例えばint型は整数値で返す。main()voidを指定しているため返り値を書かなくてもいい。)

SaitoAtsushiさんのコメントより補足しました

  1. void型以外ならreturnを省略できない。
    void function(void)(返り値なし、引数無し)void型なのでreturnは省略できる。
    int function(void)(返り値は整数、引数無し)int型の返り値が求められる。

  2. int型に指定しておいて、returnが省略できるのはmainだけの特別ルールです。

(実際の手順ではCLIを通してコンパイルしてからhello, world\n出力される。)

Compiling in detail

preprocessing:前処理。
  #...:前処理指令の始まり。
  #include:組み込み指令。(後ろに組み込みファイルを指定)
  #define:定義指令。
  ...
 ↓
compiling:アセンブル言語へ解析する。
 ↓
assembling:CPUが分かる機械語(01)へ。
 ↓
linking:ソースコードと、ソースコードで用いられたヘッダーファイルの関数へリンクし、ミックスする。

C言語の書き方では、初めのところからコンパイラに与える指令を書いてから、ほかのファイルに(保存された関数やプログラムの)協力を求めて、機械語に翻訳していくように感じています。

書き方としてはJavaScriptと違って、C言語は固定の手続きが必要だと感じて、事前に決められたデータ型しかインプットできないため、柔軟性が下がるけどとても安定していると勝手に思っています。

Practices

下は映像の例の練習:

adder: get_int()
#include <stdio.h>
#include <cs50.h>

int main(void)
{
  // get_int() is cs50 library function
  // protorype: int get_int(string prompt, ...);
  // string prompt can't be nothing
  int x = get_int("x is ");
  int y = get_int("y is ");

  // %i => check argument's data type if "int" or not
  printf("sum of x and y is %i\n", x + y);
}
// x is 1
// y is 2
// sum of x and y is 3
ints.c: get_int()
int main(void)
{
  int x = get_int("x is ");
  int y = get_int("y is ");

  printf("%i plus %i is %i\n", x, y, x + y);
  printf("%i minus %i is %i\n", x, y, x - y);
  printf("%i times %i is %i\n", x, y, x * y);
  printf("%i divided by %i is %i\n", x, y, x / y);
  printf("remainder of %i divided by %i is %i\n", x, y, x % y);
}
// x is 1
// y is 10
// 1 plus 10 is 11
// 1 minus 10 is -9
// 1 times 10 is 10
// 1 divided by 10 is 0 // incorrect
// remainder of 1 divided by 10 is 1
floats.c: get_float()
int main(void)
{
  float x = get_float("x is ");
  float y = get_float("y is ");

  printf("%f plus %f is %f\n", x, y, x + y);
  printf("%f minus %f is %f\n", x, y, x - y);
  printf("%f times %f is %f\n", x, y, x * y);
  printf("%f divided by %f is %f\n", x, y, x / y);

  // error: invalid operands to binary expression ('float' and 'float')
  // printf("remainder of %f divided by %f is %f\n", x, y, x % y);
}
// x is 1
// y is 10
// 1.000000 plus 10.000000 is 11.000000
// 1.000000 minus 10.000000 is -9.000000
// 1.000000 times 10.000000 is 10.000000
// 1.000000 divided by 10.000000 is 0.100000

(整数に対する%で剰余演算はできるが、浮動小数点数にはできません。浮動小数点数の剰余は<math.h>fmodが使える。)

sizeof演算子:

sizeof: sizeof operator
#include <stdio.h>
#include <cs50.h>

int main(void)
{
  printf("bool is %lu byte\n", sizeof(bool));
  printf("int is %lu byte\n", sizeof(int));
  printf("char is %lu byte\n", sizeof(char));
  printf("string is %lu byte\n", sizeof(string));
  printf("double is %lu byte\n", sizeof(double));
  printf("float is %lu byte\n", sizeof(float));
  printf("long long is %lu byte\n", sizeof(long long));
}
// bool is 1 byte
// int is 4 byte
// char is 1 byte
// string is 8 byte
// double is 8 byte
// float is 4 byte
// long long is 8 byte
imprecision.c-1
#include <stdio.h>

int main(void)
{
  printf("%f\n", 1 / (float)10);
  printf("%f\n", 1.0 / 10.0);
}
// 0.100000
// 0.100000

floatなら6桁まで精度が保証されているが。

imprecision.c-2
int main(void)
{
  printf("%.10f\n", 1 / (float)10);
  printf("%.10f\n", 1.0 / 10.0);
}
// 0.1000000015
// 0.1000000000

7桁から精度が失われる。けれどもっと気になるのは、下のような書き方すると計算ではまた違った結果ができました。

imprecision.c-3
int main(void)
{
  printf("%.20f\n", 1 / (float)10);
  printf("%.20f\n", 1.0 / 10.0);
}
// 0.10000000149011611938
// 0.10000000000000000555

int main(void)
{
  printf("%.20f\n", 1 / (float)10);
  printf("%.20f\n", 1.0 / (float)10);
}
// 0.10000000149011611938
// 0.10000000000000000555

int main(void)
{
  printf("%.20f\n", 1 / (float)10);
  printf("%.20f\n", 1.000000 / 10.000000);
}
// 0.10000000149011611938
// 0.10000000000000000555

(色々と試してみたけどキーワードが分からず検索しても原因がどこにあったかわからなかった。一応メモしておきます。)

SaitoAtsushiさんのコメントより補足しました

四則演算子は計算前に左右の型を揃える、また結果の型もその揃えられた型と同じになるという規則があり

  1. 計算結果が計算時の型によって型変換する。

  2. 関数に渡すときにも変換が起こる。

データ型(Data Types):

型によって固定のバイト長を有するもの。

SaitoAtsushiさんのコメントより補足しました

@SaitoAtsushi さんのコメントを見て考えさせられました。JavaScriptしか本格的に勉強していないからかな、ネット通信のこと考えるとプログラムをより小さくする傾向があって、自然と型の大きさに気になってしまいました。問題意識というより応用する場面が完全に違っていて、この行が不適切なので削除します。

  • int(singed int):符号付き整数型。一つの整数型データは常に4バイト(32bits)のメモリーを占めている。符号付きで負数を表現するため1bitを削って、残りの31bits(2^31通表現)で正数と負数を正しく表現できる範囲は-2^31 ~ 2^31-1になる。(-10も一つの表現として表されているからです。)
    • unsigned int:符号なし整数型。負数の表現が不要になって正数の使える範囲が2倍(2^31→2^32通表現)になり、0 ~ 2^32-1の正数を正しく表現できる。
  • char:文字型。一文字が1バイト(8bits)のメモリーを取っているので表現範囲は-128 ~ 127。ASCIIコードは0 ~ 127範囲で表現されている。
  • float:単精度浮動小数点型。4バイト(32bits)。符号に1bit、仮数部23bits、指数部8bits
    • FLT_MIN:float型表現可能な最小正数。1.175494e-38。
    • FLT_MAX:float型表現可能な最大値。3.402823e+38。
    • - FLT_MAX:float型表現可能な最小値(負の最大値)。
    • FLT_DIG:float型の有効桁(精度)。6。
  • double:倍精度浮動小数点型。8バイト(64bits)。符号に1bit、仮数部52bits、指数部11bits
    • DBL_MIN:double型表現可能な最小正数。2.225074e-308。
    • DBL_MAX:double型表現可能な最大値。1.797693e+308。
    • - DBL_MAX:double型表現可能な最小値。
    • DBL_DIG:double型の有効桁。15。
  • struct:構造体型。データ型を格納する型?(格納する対象がオブジェクトに見えるけど、ブーリアンなど単純の値が格納できるかどうかまだわかりません。主にintcharの集成。)
  • union:共用体型。構造体と同じくほかのデータ型を格納することができる。ただし格納する要素が同じメモリアドレスに置かれているため同時に使えません。

  • enum:列挙型。int型整数定数の参照先(変数名)の集合です。(ちょっとマップに似ている気がして)

型なし:

  • void:データがない型、空洞を示す型です。(データ/引数を入れる必要がない、それを明示するための型)

値としての型:

  • _Bool:理論型。真を1、偽を0で表す。
  • bool:理論型。<stdbool.h>によって使える値です。真をtrue、偽をfalseで表す。

型修飾子(Type qualifier):

  • short:メモリーのバイト長が2バイト(16bits)。
  • long:メモリーのバイト長が4バイト(32bits)。
  • signed:符号付き。
  • unsigned:符号なし。

変換指定子(Conversion Specification):

  • 文字の入出力
    • %c(character):char。一文字。
    • %s(string):char。文字列。
  • 10進数の入出力
    • %d(decimal):int(32bit)・short int(16bit)。整数を10進数で。
    • %ld(long decimal):long int(64bit)。倍精度整数。
    • %u(unsigned):unsigned int(32bit)・unsigned short int(16bit)。符号なし整数。
    • %lu(long unsigned):unsigned long int(64bit)。符号なし倍精度整数。
  • 8進数の入出力
    • %o(octal):int(32bit)・short(16bit)・unsigned int(32bit)・unsigned short(16bit)。整数を8進数で。
    • %lo(long octal):long int(64bit)・unsigned long int(64bit)。倍精度整数を。
  • 16進数の入出力
    • %x(hexadecimal):int(32bit)・short int(16bit)・unsigned int(32bit)・unsigned short int(16bit)。整数を16進数で。
    • %lx(long hexadecimal):long int(64bit)・unsigned long int(64bit)。倍精度整数を。
  • 浮動小数点の入出力
    • %f(float):float(32bit)。実数を入出力。
    • %lf(long float):double(64bit)。倍精度実数を入出力。(すでに頭文字のdが使われてるのでl(long)で2倍というかもしれません。)
    • %e(exponentiation):float(32bit)。指数表示で実数を出力。(出力だけ)
    • %gfloat(32bit)。最適な形式で実数を出力。(出力だけ)

#define

マジックナンバーなどを定数にする。指定した文字列(大文字)に値や式として置き換える。

// #define NAME value(integer/string/expression...)
#define CARDSIZE 52 // integer
#define COURSE "CS50" // string
#define PI 3.14f // float

Memo

int num; // declaration => uninitialized
num = 17; // assignment => initialized
char letter;  // declaration => uninitialized
letter = 'abc'; // assignment => initialized
//
int num = 17; // initialization
char letter = 'abc'; // initialization

運算子(Operators):

算術演算子(Arithmetic operators):

  • +(add):加算演算子。
  • -(subtract):減算演算子。
  • *(multiply):乗算演算子。
  • /(divide):除算演算子。
  • %(get remainder):剰余演算子(モジュロ演算子)。除算で余った数を返す。

Shorthand operators

  • x += 1x = x + 1
  • x -= 1x = x - 1
  • x *= 2x = x * 2
  • x /= 2x = x / 2
  • x %= 2x = x % 2
#include <cs50.h>
#include <stdio.h>

int main(void)
{
  int a = 2;
  int b = 3;

  a += 1; // a = a + 1 => a = 2 + 1
  b -= 2; // b = b - 2 => b = 3 - 2

  printf("a is %i\n", a);
  printf("b is %i\n", b);
}
// a is 3
// b is 1
int main(void)
{
  int x = 1;
  int y = get_int("Input: "); // cs50 library
  int z = 15;

  x = x * 5; // 1 * 5
  x *= 5;    // 5 * 5

  y = y / 3; // 12 / 3
  y /= 2;    // 4 / 2

  printf("x is %i\n", x);
  printf("y is %i\n", y);
  printf("z is %i\n", z %= 2);
}
// input 12
// x is 25
// y is 2
// z is 1
int main(void)
{
  int a = 2;
  int b = 3;

  a++; // a = a + 1
  b--; // b = b - 1

  printf("a is %i\n", a);
  printf("b is %i\n", b);
}
// a is 3
// b is 2

ブール式(Boolean Expressions):

  • true/non-zerotrueまたは0ではない値を真値(true value)として扱われる。
  • false/0falseまたは0は偽値。

SaitoAtsushiさんのコメントより補足しました
「ブール式(Boolean Expressions)」という言葉は映像の中から取り上げたもので英語そのまま訳語として使いました。言葉としてあまり使われていなく、「**制御式 (controlling expression)」という名前が広く使われていると指摘をいただきました。本文では映像からの勉強メモなので一貫性を保つため「ブール式」そのまま使いますが、正式の用語ではありません。

論理演算子(logical operators):

  • !x(NOT):xではない。xtrueなら!xfalseになる。xfalseなら!xtrueになる。論理否定。
  • x && y(AND):xy両方がtrueであればtrue。論理積。
  • x || y(OR):xyどちらがtrueであればtrue。論理和。

比較演算子(relational operators):

  • x < y(less than):xyより小さい
  • x <= y(less than or equal to):xy(を含めて)以下
  • x > y(greater than):xyより大きい
  • x >= y(greater than or equal to):xy(を含めて)以上

等値演算子(Equality & InEquality)

  • x == yxyと同じならtrue、でなければfalse
  • x != yxyと同じではないならtrue、同じならfalse

優先順位と結合規則

行の長さ(Line length)、字下げ(Indentation)

行の長さ:一行に80字。
字下げ:4文字幅。
(JavaScriptの書き方になれていて、どうしても4スペースが読みづらくてスキップします。)

Conditions

条件分岐(if, Conditional branch with Boolean expressions)

排他的条件分岐(mutually exclusive branchs):

if条件式は上から下へブール式がtrueであるかを判定する、一番早くtrueが出てくるブール式での波括弧のコードを行う(一つの結果だけに導く)。

// two branch
if (boolean-expression)
{
    // if boolean expression is true, execute all lines of code in curly bracket
}
else
{
    // if boolean expression is false, execute here
}

// two or more branch
if (boolean-expr1)
{
    // first branch
}
else if (boolean-expr2)
{
    // second branch
}
else if (boolean-expr3)
{
    // third branch
}
else
{
    // fourth branch
}
#include <stdio.h>
#include <cs50.h>

int main(void)
{
  int x = get_int("Give me a number: "); // cs50 library
  if (x > 0)
  {
    printf("x is positive\n");
  }
  else if (x < 0)
  {
    printf("x is negative\n");
  }
  else
  {
    printf("x is zero\n");
  }
}

独立的条件分岐(non-mutually exclusive branchs):

それぞれの条件式が自分のブール式の判定をし、複数の結果が出る可能性があります。

#include <stdio.h>
#include <cs50.h>

int main(void)
{
  int x = get_int("Let's check the number: ");
  if (x < 0)
  {
    printf("OK, x is negative.\n");
  }
  if (x > 0)
  {
    printf("OK, x is positive.\n");
  }
  if (x > 5)
  {
    printf("Excellent, x is bigger than five!\n");
  }
  else
  {
    printf("Well, x is smaller than five.\n");
  }
}
// "Let's check the number: 2
// OK, x is positive.
// Well, x is smaller than five.

// "Let's check the number: 10
// OK, x is positive.
// Excellent, x is bigger than five!

ケース対応分岐(switch, Conditional statement with discrete cases)

ifと違ってswitchでは波括弧に想定しているケースを書いておき、breakを付ける/付けないことによって一つのケースとして出力するか、数値を通して残りのケースを順番通り行う。(fall throughを利用する)

#include <stdio.h>
#include <cs50.h>

int main(void)
{
  // int x = get_int("Let's check where to go: ");
  int x = get_char("Input the first letter of city name: ");

  switch (x)
  {
  case 1:
  case 'T':
  case 't':
    printf("The first stop is Tokyo.\n");
  case 2:
  case 'N':
  case 'n':
    printf("The second stop is Nagoya.\n");
  case 3:
  case 'O':
  case 'o':
    printf("The third stop is Osaka.\n");
  case 4:
  case 'K':
  case 'k':
    printf("The fourth stop is Kyoto.\n");
  default:
    printf("Have a nice trip!\n");
  }
}

// Input the first letter of city name: n
// The second stop is Nagoya.
// The third stop is Osaka.
// The fourth stop is Kyoto.
// Have a nice trip!
int main(void)
{
  int x = get_int("Only one fruit you can choose: ");

  switch (x)
  {
  case 1:
    printf("Number 1 is Apple\n");
    break;
  case 2:
    printf("Number 2 is Banana\n");
    break;
  case 3:
    printf("Number 3 is Kiwi\n");
  default:
    printf("I'm still hungry...\n");
    break;
  }
}

// Only one fruit you can choose: 2
// Number 2 is Banana

// Only one fruit you can choose: 3
// Number 3 is Kiwi
// I'm still hungry...

三項演算子(ternary operator)

SaitoAtsushiさんのコメントより補足しました
「三項演算子(ternary operator)」も映像の中から取り上げた用語です。C言語では「条件演算子」のほうが適切だと指摘を頂きました。適切な用語ではありませんが、これも一貫性のためそのままにしておきたいと思います。

  • expression ? A : B:はてな前の式がtrueであればAを返す。falseであればBを返す。
#include <stdio.h>

int main(void)
{
  int x;
  printf("How old are you: ");

  // specify type(%i=>int), if it's true, assign input to x
  scanf("%i", &x);

  x >= 18 ? printf("%i is Adult.\n", x) : printf("%i is under age\n", x);
}
// How old are you: 17
// 17 is under age

// How old are you: 20
// 20 is Adult.

Loops

for

#include <stdio.h>

int main(void)
{
  for (int i = 0; i < 3; i++)
  {
    printf("i is %i\n", i);
  }
}
// i is 0
// i is 1
// i is 2
int main(void)
{
  int j;
  printf("Please input the length: ");

  scanf("%i", &j);
  for (int i = 0; i < j; i++)
  {
    printf("i is %i\n", i);
  }
}
// Please input the length: 3
// i is 0
// i is 1
// i is 2

forループの実行順序:

for (start; boolean-expression; increment/decrement...)
{
  // do something here
}

start
 ↓
boolean-expression
 ↓ if true
do something in {}
 ↓
increment/decrement...
 ↓
boolean-expression
 ↓ if true
do something in {}
 ↓
increment/decrement...
 ↓
boolean-expression
 ↓ if false
end the loop

while

int main(void)
{
  int i = 1;
  int sum = 0;
  while (sum <= 5)
  {
    sum = sum + i;
    printf("(i, sum) = (%d, %d)\n", i, sum);
  }
}
// (i, sum) = (1, 1)
// (i, sum) = (1, 2)
// (i, sum) = (1, 3)
// (i, sum) = (1, 4)
// (i, sum) = (1, 5)
// (i, sum) = (1, 6)

while(boolean-expr)はブール式の真偽判断してからコードを実行する。

do...while

int main(void)
{
  int i = 1;
  int sum = 0;
  do
  {
    sum = sum + i;
    printf("(i, sum) = (%d, %d)\n", i, sum);
  } while (sum <= 5);
}
// (i, sum) = (1, 1)
// (i, sum) = (1, 2)
// (i, sum) = (1, 3)
// (i, sum) = (1, 4)
// (i, sum) = (1, 5)
// (i, sum) = (1, 6)

do...while(boolean-expr)は先にコード実行してからブール式の判断する。

while vs. do...while

int main(void)
{
  int i = 1;
  int sum = 10;
  do
  {
    sum = sum + i;
    printf("(i, sum) = (%d, %d)\n", i, sum);
  } while (sum <= 5);
}
// (i, sum) = (1, 11)
// note: run at least one time

do...whileのようにブーリアン判断が後ですると、せめて一回の実行が確保できる。

int main(void)
{
  int i = 1;
  int sum = 10;
  while (sum <= 5)
  {
    sum = sum + i;
    printf("(i, sum) = (%d, %d)\n", i, sum);
  }
}
// *nothing happend

whileは先に判断を行うので何も表示されなかった。

0
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?