0
1

初めてのC言語 ~Javaとの違い~

Last updated at Posted at 2024-07-31

はじめに

この記事は

  • 初めてC言語を学ばれる方
  • Javaを学んで次にC言語を学ぶ方

に向けて作成しました。

今までJavaを主に勉強していて、新たにC言語を学び始めたのでアウトプットも兼ねて、
Javaとの違いを交えながら説明できたらと思います。

「この記事を見たらC言語が出来る」というより、
C言語を勉強する前にどういうものか知りたい
といった目的に合う記事だと思います。

初めてのQiita投稿になりますので、不慣れな点もあるかと思いますが、
温かい目で見ていただけると嬉しいです。

C言語ってなに?

C言語は、主流のプログラミング言語の中で最も古く、多くの言語がC言語を
ベースに作られています。

言語名 特徴
C 最も古い主流言語。多くの言語のベース。
C++ C言語を拡張。オブジェクト指向対応。
Java C/C++ベース。Sun Microsystemsが開発。Androidで使用。
C# MicrosoftがJavaやC++を基に独自開発。

活用例

  • OS開発/基幹システム
    ハードウェア制御が可能なため、OSや基幹システムの開発に使用されます。
    例:MacOS X、Linuxカーネル

  • IoT開発
    実行ファイルが小さく、高速に処理できるため、IoTやデバイスの制御に適しています。

  • ロボット / 組み込み系開発
    CPUやメモリが搭載されたハードウェアの制御に使用されます。
    例:教育用ロボット、産業用ロボット、電化製品

文法の特徴

C言語はプログラム実行順にコードを記述する手続き型言語です。

  • 処理速度が高速
    コンパイルされた機械語をそのまま実行するため、高速な処理が可能です。

  • 高い汎用性
    ハードウェア制御が得意で、CPUの負荷やメモリ消費が少ないプログラミング言語です。
    これにより、あらゆる機器への組み込みが可能です。

  • さまざまなOS・プロセッサで実行できる
    特定のOSやプロセッサに依存しない仕様です。

  • 開発を効率化するライブラリが豊富
    便利なプログラムを再利用できるライブラリが多数存在します。

開発環境

開発環境
C言語 コンパイラ、テキストエディタ Visual Studio (Windows), Xcode (Mac), Eclipse
Java 総合開発環境(IDE)、Java開発キット(JDK) Eclipse, IntelliJ IDEA, Visual Studio Code

C言語とJavaの違い

標準ライブラリ

C言語では、標準ライブラリの機能を使用するために、ヘッダファイルをインクルードします。
(インクルードとはファイルの中に別のファイルに記述された内容を追加する機能)

C
#include <stdio.h> 
// 標準入出力ライブラリ。例:printf, scanf

#include <stdlib.h>
// メモリの動的割当や乱数生成。例:malloc, rand

#include <string.h>
// 文字列操作のための関数。例:strcpy, strcat, strlen

#include <math.h>
// 数学関数。例:sin, cos, sqrt

Javaでは、標準ライブラリの機能を使用するために、パッケージをインポートします。

Java
import java.lang.*;
// 基本的なクラスやインターフェース。例:String, Object, System

import java.util.*
// データ構造など。例:ArrayList, HashMap

import java.io.*;
// ファイル入出力など。

アクセス修飾子

C言語にはJavaのような言語で見られる特定のアクセス制御キーワード(public、private、protectedなど)はありません。

しかしstaticを使い関数や変数のスコープはファイルやブロックによって制御すること出来ます。
関数はファイル内でのみアクセス可能ですが、ヘッダファイルをインクルードすることで
他のファイルから利用できます。

example.h
void myFunction();
example.c
#include "example.h"
void myFunction() {
    // 関数の実装
}
main.c
#include "example.h"
int main() {
    myFunction(); // example.cの関数を呼び出し
    return 0;
}

Javaでは、アクセス修飾子(public,protected,private)を使用して クラスやメソッドのアクセス範囲を制御します。
Java
public class Example {
    private int value;
    public void setValue(int value) {
        this.value = value;
    }
    public int getValue() {
        return value;
    }
}

データの構造化

C言語では、異なる型のデータをひとまとめにするために構造体(struct)を使用します。

C
// 構造体の宣言
struct Person {
    char name[50];
    int age;
};

構造体のメンバにアクセスするためには、ドット演算子(.)を使用し、
ポインタ(後で説明あり)を使用する場合はアロー演算子(->)を使用します。

C
struct Person person; // 構造体の定義
// 構造体のメンバへ代入
strcpy(person.name, "太郎");
person.age = 30;
printf("%sは%d歳です。\n", person.name, person.age);

ポインタを使用した場合のメンバへのアクセス

C
struct Person person; // 構造体の定義
struct Person *ptr = &person; // 構造体へのポインタ
// ポインタを使用して構造体のメンバへ代入
strcpy(ptr->name, "太郎");
ptr->age = 30;
printf("%sは%d歳です。\n", ptr->name, ptr->age);
出力結果
太郎は30歳です。

Javaでは、クラスを使用してデータとその操作をカプセル化します。 クラスはフィールド、メソッド、コンストラクタを持ちます。
Java
public class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

標準出力

C言語では、printf関数を使用して標準出力にメッセージを表示します。
出力の際、データ型の指定が必要です。

書式指定例

書式 意味
%d 整数値を10進数で表示
%f 実数値を10進数で表示
%c 文字を表示
%s 文字列をそのまま表示
C
#include <stdio.h>

int main() {
    int num = 10;
    printf("Number:%d\n", num);
    return 0;
}
出力結果
Number:10

Javaでは、System.out.printlnメソッドを使用して標準出力にメッセージを表示します。 出力の際、型の再指定は不要です。
Java
public class Main {
    public static void main(String[] args) {
        int num = 10;
        System.out.println("Number: " + num);
    }
}
出力結果
Number:10

文字列

C言語では、文字列はchar型の配列として表現されます。

C
char str[] = "Hello, World!";

C言語では、+演算子を使用して文字列を連結することはできません。

その代わりに、strcatなどの関数を使用して文字列操作を行うのが一般的です。
文字列操作のために以下のようなライブラリ関数が用意されています。

strlen 文字列の長さを取得
strcpy 文字列のコピー
strcat 文字列の連結
strcmp 文字列の比較

Javaでは、文字列はStringクラスとして扱われます。
Java
String str = "Hello, World!";

C言語特有の概念・記述

プロトタイプ宣言

C言語では、プログラムは上から下に順番に処理されます。
そのため、先に宣言や定義をしていない変数や関数を使うと、コンパイル時にエラーが発生します。

しかし、プログラムが大きくなると、すべての関数の定義が先に来るとは限りません。
そんなときに役立つのが「関数のプロトタイプ宣言」です。

プロトタイプ宣言とは、関数の戻り値の型関数名引数の型をプログラムの冒頭で宣言しておくことです。
これにより、実際の関数定義がプログラムの後半にあっても、その関数を前半で呼び出すことが可能になります。

#include <stdio.h>

// 関数のプロトタイプ宣言
int square(int num);
void print_square(int num);

// main関数
int main(void) {
    int number = 5;
    print_square(number);
    return 0;
}

// 関数の定義
int square(int num) {
    return num * num;
}

void print_square(int num) {
    int result = square(num);
    printf("%d の二乗は %d です。\n", num, result);
}

変数宣言によるメモリ領域確保のイメージ

image.png

ポインタとアドレス

アドレスとは?
  • 変数を格納している領域のようなもの。変数の住所とよく言われている
  • コンピュータはプログラム内の変数をメモリ上に確保し、その場所を特定するための
    アドレスを記憶している
ポインタとは?
  • アドレス(住所)を指し示すための変数
  • ポインタは、英語で「ある場所を指し示す人や物」という意味
    つまり、ポインタ型の変数を宣言するということは、「0012FF7C」などの、
    住所を指し示す値を入れる箱を用意することを意味する

Javaは高レベルのプログラミング言語であり、メモリ管理を自動化し、安全性と移植性を高めるためにポインタの概念を排除しています。
しかし、ポインタに似た概念として参照(reference)が存在します。

#include <stdio.h>

int main() {
    int a = 100;        // int型の変数
    double b = 123.4;   // double型の変数
    float c = 123.4f;   // float型の変数(数値の後ろにfをつける)
    char d = 'a';       // char型の変数
    
    // aという変数があったら&aとすることにより、変数のアドレスを取得できる
    printf("aの値は%d、 大きさは%zdbyte、 アドレスは%p\n", a, sizeof(int), &a);
    printf("bの値は%f、 大きさは%zdbyte、 アドレスは%p\n", b, sizeof(double), &b);
    printf("cの値は%f、 大きさは%zdbyte、 アドレスは%p\n", c, sizeof(float), &c);
    printf("dの値は%c、 大きさは%zdbyte、 アドレスは%p\n", d, sizeof(char), &d);
    
    return 0;
}
出力結果
aの値は100、 大きさは4byte、 アドレスは0x8c8ff654
bの値は123.400000、 大きさは8byte、 アドレスは0x8c8ff678
cの値は123.400002、 大きさは4byte、 アドレスは0x8c8ff694
dの値はa、 大きさは1byte、 アドレスは0x8c8ff6b4
  • 上記のようにどの変数にもアドレスがあり、型に依存した固有のサイズがあります
  • アドレスは16進数の4バイトか8バイトで表示されます。(32bitか64bitシステムで変わる)
実際にポインタとアドレスを使ってみる
#include <stdio.h>

int main() {
    int a = 100;    // 整数型変数a
    int b = 200;    // 整数型変数b
    int* p = NULL;  // 整数型のポインタ変数p(ポインタ変数はNULLで初期化する)

    p = &a; // pにaのアドレスを代入
    printf("a = %d b = %d *p = %d\n", a, b, *p);
    
    *p = 300; // *pに値を代入
    printf("a = %d b = %d *p = %d\n", a, b, *p);

    p = &b; // pにbのアドレスを代入
    printf("a = %d b = %d *p = %d\n", a, b, *p);

    *p = 400; // *pに値を代入
    printf("a = %d b = %d *p = %d\n", a, b, *p);
}
出力結果
a=100 b=200 *p=100
a=300 b=200 *p=300
a=300 b=200 *p=200
a=300 b=400 *p=400

このように、ポインタ変数は、初期状態でそれ自身は値を持ちませんが、変数のアドレスを与えることにより、あたかもその変数であるかのごとく振舞うことが出来るのです。

したがって、上記のサンプルのように、同じアドレスを取得していると値も同じ値になったり、片方の変数(*p)の値しか変えていないのが同じアドレスなので、もう一方の変数(a)も変わったりします。

おわりに

C言語の特徴や、Javaとの違い、特有概念についてまとめてみました。
ポインタとアドレスに関してはこれを見るだけではまだ難しいと思いますが、
理解の助けになれたら幸いです。
以上になりますが、ここまでご閲覧ありがとうございました!

参考資料

C言語構造体の理解のための自分への備忘録
【C言語】関数のプロトタイプ宣言を書く理由
【初学者向け】C言語のポインタとアドレスについてまとめてみた。
【C言語入門】関数の作り方、呼び出し方(宣言、引数、戻り値)

0
1
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
1