(C言語)文字配列と文字列の違い。

フォーマット演算子等についての違いが知りたい方は、以下の記事で書いてますので是非参考にしてみてください。

bluecat314.hatenablog.com

始めに

上の記事で int array [ ] int* arrayと説明し、 ニアリーイコールがついていますがこのことについて話します。

どちらともぱっと見キャストとして使うことが可能ですが、内部の構造が若干違います。

実は

  • char array[ ]="abcde"<=>char array[ ]={"a","b","c","d","e"}となっています。
  • char* array<=>const char* arrayはそのままです。

この大きな差は

  1. 領域なのか値なのかです。
  2. スコープ(範囲)が異なる

ここで問題になるのが値が代入できない点です。

詳しく見ていきましょう

代入できない

char a[ ]="abcde";
char* b="ABCDE";
b=a;
printf("%s\n",b);

結果

abcde

これはできる。じゃあ逆は??

char a[ ]="abcde";
char* b="ABCDE";
a=b; //ここが違う
printf("%s\n",b);

結果

 error: assignment to expression with array type
    8 |   a = b;
      |     ^

出来ない。。

このように代入できないときが発生します。

これはaは[ ]で定義しており、[ ] は領域を指し、アドレスを格納するメモリが存在しないからです。

補足

a[0]=v等のことならできます。

スコープが異なる

パターン1

char* aa(){
char* array="abcde";//ここが*
return array;
}

void main(){
puts(aa());
}

結果

abcde

パターン2

char* aa(){
char array[ ]="abcde";//ここが[ ]
return array;
}

void main(){
puts(aa());
}

結果

 warning: function returns address of local variable [-Wreturn-local-addr]
    6 |   return array;
      |          ^~~~~

エラーが出てしまいました。

このエラーは返り値でローカル変数返すなよ。。と言っていてスコープが狭いことを意味します。

こういったところで注意が必要になります。

まとめ

  • [ ]で作った文字配列はアドレスの変更ができない。(1つ1つの変更なら可能)
  • [ ]で作った文字配列はスコープが自分自身のみ

参考

c++ - char *a と char b[] にはどのような違いがありますか - スタック・オーバーフロー

徹底図解!C言語のポインタと配列の違いを解説! | だえうホームページ

(C言語)2重ポインタについて

 2重ポインタについて少しですがわかりやすくまとめて置きます。

そもそもポインタとは、

変数を定義したときに変数はアドレスというものを持っていて、その別名として変数名があるような形になっています。 このアドレスをつかさどるのがポインタです。

ポインタを使うことで変数をアドレスとして使うことができます。

ここで変数名で使えばいいやんと思うかもしれないが、c言語には文字列という概念が備わっていないので、アドレスを活用して文字列を使います。

1重のポインタ

書き方

int a=10;
int\* p; //int \*pでも大丈夫
p=&a; //&は後ろの変数のアドレスを読み取るものです。
printf("%d\n",\*p);// \*はアドレス値から値を読み取ります。

詳しく日本語で説明

  1. 整数aの値を10とする。(10は値であってアドレスではないです。)
  2. 整数ポインタ変数pを宣言する。(ポインタ変数にもcharの型etc..があります。)
  3. 整数ポインタ変数pにaのアドレス値を代入します。
  4. 整数ポインタ変数pに入っている数値(アドレス)の値を出力します。

これは1重のポインタです。

  • *(宣言時)・・・ポインタ変数ですよって意味
  • &・・・変数からアドレスと読み取る
  • * ・・・アドレスから変数(の値)を読み取る

(アドレス値は適当です。)

&と*は対義語みたいなものです。

2重ポインタ

書き方

int a=10;
int\* p; 
int\* pp; 
p=&a; //&は後ろの変数のアドレスを読み取るものです。
pp=&p
printf("%d\n",\*\*pp);// \*はアドレス値から値を読み取ります。

詳しく日本語で説明

  1. 整数aの値を10とする。
  2. 整数ポインタ変数pを宣言する。
  3. 整数ポインタ変数ppを宣言する。<-違うところ
  4. 整数ポインタ変数pにaのアドレス値を代入します。
  5. 整数ポインタ変数ppにpのアドレス値を代入します。
  6. 整数ポインタ変数ppに入っている数値(アドレス)の値を出力します。

(アドレス値は適当です。)

最後なんて**となっていて*が2つもあります。

この*の数で何重ポインタと言っているのです。

↓↓こんな感じです。。↓↓

まとめ

  • *&は対義語
  • わからなくなったら一つ一つ置換(代入するとわかりやすい)

(C言語) 配列宣言の仕方([] *)

C言語の配列

c言語の配列で1年弱でようやく理解したのでまとめました。

  • 配列のキャストについて
  • printf時のフォーマット指定子

配列のキャスト

軽いまとめ

  • int array≒int* array

  • char array≒char* array

[] , *をすることでキャストを宣言します。

補足
キャストとは...
なんでも入ってよい箱(配列,変数)ではなく、専用の箱を作るイメージ この箱には整数のみ、この箱には小数のみetc...

なぜなのかは下の記事について詳しく書きます。

bluecat314.hatenablog.com



じゃあなかったらどうなるの??

char array="abcd";で宣言をする

void main(){ //#include <stdio.h>をしています
  char array="abcd";
  printf("%s",array);
}

結果

test.c: In function ‘main’:
test.c:6:16: warning: initialization of ‘char’ from ‘char *’ makes integer from pointer without a cast [-Wint-conversio
]
    3 |   char array = "abcd";
      |                ^~~~~~

これはキャストされていないことを意味し、おいおい1文字しか受け付けないところに2文字以上入れるなよ的な感じです。

ここでchar array[]またはchar* array にすることでキャストされうまく実行いたします。

void main(){
  char array1[]="abcd"; //[]でキャスト
  char* array2="abcd"; //*でキャスト
  printf("%s",array1);
  printf("%s",array2);
}

結果

abcd
abcd

printfの%d,%c,%sの違い

%d,%cはそれぞれ一文字の数字と文字対応 %sは文字列に対応

簡単じゃんと思うかもしれないが、変数の指定の仕方に注意。。。

char* array="abcd";
printf("%c\n",*array);//*あり
printf("%s\n",array);//*なし

結果

a
abcd

array=array[0]を意味します。 もし2番目のbを取り出したければ(array+1) or array[1]することでうまくいきます。

char* array="abcd";
printf("%c\n",*(array+1));//*あり
printf("%c\n",array[1]);//*なし

結果

b
b

このように配列名は先頭のポインタを指しますがその際*を付けるのか付かないのかで意味が大きく変わってくるので注意が必要です。

まとめ

  • 配列を宣言する時は[]または*をつける
  • 文字列を表示する時は*なし。1文字のみの時は*あり

(Rails) deviselogin出来ないときの対処法(devise)

状況

sign upはできるがloginができない。

(Sign upはname,email,passwordで登録し、Log in時はname,passwordで認証する。)

原因

ストロングパラメーターは設定していたが、/config/initializers/devise.rbのほうを変えていなかった

apprication_controller

before_action :configure_permitted_parameters, if: :devise_controller?
private
  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:sign_up, keys: [:name])#変更前
    devise_parameter_sanitizer.permit(:sign_up, keys: [:email])#変更後
  end

devise.rb

config.authentication_keys = [:email]#変更前
config.authentication_keys = [:name]#変更後

詳細

emailとnameの指定が逆だった。

意味としては、apprication_controllerでsign up時にemail登録を許可し、devise.rbでuser認証時のチェックをnameで行いますよ

ここで実験

  1. devise.rb->email,apprication_controller->nameの場合
  2. devise.rb->email,apprication_controller->emailの場合
  3. devise.rb->name,apprication_controller->nameの場合
  4. devise.rb->name,apprication_controller->emailの場合

error

  1. 成功
  2. 登録時::name can't be blank,ログイン時::Unpermitted parameter: :name
  3. 登録時::Email can't be blank
  4. ログイン時::Unpermitted parameter: :name

実験からの考察

user認証をするときに値を取得するのはdevise.rbのほうで行い、 登録する時はdevise.rb,apprication_controller両方を用いる。

まとめ

email以外で認証する時は、devise.rbも変えよう!

(Rails) @book.user_idと@book.userの違い

model

book user
id id
book name
user_id

注意::userとbookには1:Nの関係がある。

状況

この時、bookからuserを指定する時に2通りの書き方ができるように思える。

@book.user_idと@book.userである。(@bookにはidが1つの入る)

しかし、これらは異なるものである↓↓

@book.user_id・・・user_id

@book.user・・・Userのレコード

詳細

@book.user_id #@bookでレコードを指定し、そのレコードのカラムであるuser_idを指定する。
@book.user #@bookでレコードを指定し、そのuser_id=user.idのuserのレコードをしてする。

@book.user_id @bookでレコードを指定(水色)し、.user_idでuser_idを指定(赤色)する。 @book.user @bookでレコードを指定(水色)し、.userでuser_idと同じuserの中のidを指定(赤色)する。そしてそのレコードを指定(黄色)する

図による違いの説明 上が@book.user_idで下が@book.userである

おまけ

modelに関数を書いた際は、@book.userの方でUserのレコードを指定しないと使えないことに注意。

まとめ

@book.user_id・・・user_id

@book.user・・・Userのレコード

def display_name(user_id)
  user_id.name
end

(Rails) form_with modelではなくform_with urlを使いこなす

状況

投稿機能を作った際にcontrollerの中でindexとcreateの2つの部分でmodel名.newが使われていた。

また、全てのviewのアクションに対して、model名.newを用意するのが面倒である(before_action等を使えば防げるが...)

これは「rbの基本として、重複は避けるとある。」に反すると思い、回避できるのかを試してみた。

補足

indexのmodel名.newは「この箱に入れてください」という仮の箱を用意し、

createのmodel名.newは「仮の箱から取り出して正式な箱に入れてください」という感じ

結論

<%=form_with model:%>と<%=form_with url:%>を使い分けることで実現する。推奨ではない。

詳細

<%=form_with model:%>と<%=form_with url:%>の差

イメージとしてはもともとmodelの型(値を入れる箱)を作るか作らないかの差

そもそも <%=form_with model:%>と<%=form_with url:%>の違いはdbに保存するかどうかである。 簡単に言うとdbに保存する時に分かりやすくするかの違いである。

また、違いを別の言い方をすると、情報を送った時に2重のハッシュにするかどうかである。

投稿フォーム

view

# ココに<%=form_with ...%>が入る #model or url
<%=f.text_field :name%>
<%=f.submit "send"%>

<%=form_with model:%>の場合

出力結果

Parameters: {"authenticity_token"=>"[FILTERED]", "newname"=>{"name"=>"model"}, "commit"=>"send"}
# 2つめの"newname"=>{"name"=>"model"}に注目

<%=form_with url:%>の場合

出力結果

Parameters: {"authenticity_token"=>"[FILTERED]", "name"=>"url", "commit"=>"send"}
# 2つめの"name"=>"url" に注目

nameの部分が違うことがわかる

この時、<%=form_with url:%>の方はコントローラーのcreate以外でnewを呼び出す必要がない。➡重複しない

この際、アクションのcreateで用いるparams部分で注意が必要であり、

<%=form_with model:%>の場合

params.require(:newname)permit(:name)

<%=form_with url:%>の場合

params.permit(:name)

となる。 これはrequire部分で、2つ目のハッシュを指定するためである。

まとめ

controller内のmodel.newの重複を防ぐことを試していた。

<%=form_with url:%>を用いることで重複を防ぎいちいちmodel名.newを作成するのを防いだ。

この時、paramsに気を付けないとエラーとなる。

(Rails)全てのviewで使えるインスタンス変数の作成

状況

application_controllerにてインスタンス変数を定義したがうまく引き継がれない

controller

class ApplicationController < ActionController::Base
  @variable='10'
end

原因

コールバック(before_action)をしていなかったため。

実施したこと

メソッドの中に変数を定義した。

よって以下のようになる

controller

class ApplicationController < ActionController::Base
    before_action :setting

    private

    def setting
        @variable='10'
    end
end

このようにすることで無事格納される。

まとめ

変数を定義する際はメソッドの中に書くのする必要がある。