我有一个小问题。 我有这段代码:
#include <stdio.h>
int main(){
printf("%d, %f, %d\n", 0.9, 10, 'C');
}
输出是这样的:
10, 0.900000, 67
但我原以为:
0, 10.0, 67
但看起来printf在表达式中搜索相应的类型(int和float被转换) 任何人都可以帮我解决这个问题吗? 非常感谢你!
答案 0 :(得分:2)
但我原以为:......
当格式字符串按顺序与参数类型不匹配时,行为未定义。任何事情都可能发生。你不能指望任何事情(我不明白为什么你会期望0.也许你希望printf
使用格式字符串来转换浮点数和整数之间的参数。它只是没有。它是一个可变函数就像你可以编写自己的,并且格式字符串编码尾随参数的类型的事实不用于转换它们。)
现代调用约定对前几个参数使用寄存器,甚至可以使用寄存器来实现可变参数函数。在x86-64上,约定可以是例如可以在浮点寄存器xmm0
中始终期望可变参数函数的第一个浮点参数,而整数参数在通用寄存器{{}中传递。 1}},%rdi
,%rsi
,...这具有%rdx
打印浮点参数后跟整数参数的效果。
作为一个例子,这是一个简短的程序:
printf("%f %d", 1, 1.0)
这就是我的编译器(Mac OS X 10.6上的Clang)编译程序的方法:
#include <stdio.h>
int main(){
printf("%d, %f, %d\n", 0.9, 10, 'C');
printf("%d, %f, %d\n", 10, 0.9, 'C');
}
很明显,这两个调用会产生相同的结果。但就一个而言,它是偶然的,只对这个特定的编译器版本和ABI“起作用”,而另一个尊重标准并且必须在任何地方工作。
同样,leaq <memory location of format string>, %rbx
movq %rbx, %rdi
movsd <memory location of 0.9 constant>, %xmm0
movl $10, %esi
movl $67, %edx
movb $1, %al
callq _printf
movq %rbx, %rdi
movl $10, %esi
movsd <memory location of 0.9 constant>, %xmm0
movl $67, %edx
movb $1, %al
callq _printf
…
是未定义的行为,您不应在任何情况下使用它。
答案 1 :(得分:1)
唯一正确的期望是67
使用%d
格式说明符 * 打印字符。另外两个打印输出是未定义的行为。
看起来像printf在表达式中搜索相应的类型
这只是巧合。 printf
不知道您传递的实际参数的类型。它信任格式字符串,并按顺序解释数据。您可以通过提供不同的数字来判断发生了什么,并观察输出的变化情况。
您看到的数字是垃圾 - double
被重新解释为int
,而int
被重新解释为double
。此外,如果double
和int
的大小不同,前两个参数会互相交叉。
要生成您想要的输出,请为前两个参数表达式添加强制转换:
printf("%d, %f, %d\n", (int)0.9, (double)10, 'C');
请注意,您不需要强制转换最后一个参数,因为char
会在处理int
的变量长度参数列表时被提升为printf
。
* 只有当其他参数和格式说明符之间没有不匹配时,才会产生正确的行为。你的程序有UB,即使是最后一个参数,如果单独使用它也是正确的。
答案 2 :(得分:1)
这是未定义的行为,所以任何事情都可能发生。
import android.app.Activity;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class MyDBHandler extends SQLiteOpenHelper {
public static final int DATABASE_VERSION = 3;
public static final String DATABASE_NAME = "favourites.db";
public static final String TABLE_FAVOURITES = "favourites";
public static final String COLUMN_ID = "_id";
public static final String COLUMN_VIDEONAME = "videoname";
public static final String COLUMN_LINK = "link";
public MyDBHandler(Context context, String name, String link, SQLiteDatabase.CursorFactory factory, int version) {
super(context, DATABASE_NAME, factory, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
String query = "CREATE TABLE " + TABLE_FAVOURITES + "(" +
COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
COLUMN_VIDEONAME + " TEXT, " + COLUMN_LINK + " TEXT " +
");";
db.execSQL(query);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + TABLE_FAVOURITES);
onCreate(db);
}
public void addFavourites(favourites favourites) {
ContentValues values = new ContentValues();
values.put(COLUMN_VIDEONAME, favourites.get_videoname());
values.put(COLUMN_LINK, favourites.get_link());
SQLiteDatabase db = getWritableDatabase();
db.insert(TABLE_FAVOURITES, null,values);
db.close();
}
public void deleteFavourites(String videoName, String link) {
SQLiteDatabase db = getWritableDatabase();
db.execSQL("DELETE FROM " + TABLE_FAVOURITES + " WHERE " + COLUMN_VIDEONAME + "=\"" + videoName + "\" "
+ COLUMN_LINK + "=\"" + link + "\" ;"
);
}
public String databaseToString() {
String dbString = "";
SQLiteDatabase db = getWritableDatabase();
String query = "SELECT * FROM " + TABLE_FAVOURITES + " WHERE 1";
//Cursor points to a location in your results
Cursor c = db.rawQuery(query, null);
//Move to the first row in your results
c.moveToFirst();
//Position after the last row means the end of the results
while (!c.isAfterLast()) {
if (c.getString(c.getColumnIndex("videoname")) != null) {
dbString += c.getString(c.getColumnIndex("videoname"));
dbString += "\n";
}
c.moveToNext();
}
db.close();
return dbString;
}
}
需要%d
,但您传递的是int
(而不是double
),因此float
占用{{1}的4个字节}}并将其解释为printf
。
double
需要int
,但您传递的是%f
。因此它需要来自下一个内存的double
和4个字节的4个字节,并将其解释为int
。
幸运的是传递16个字节,而int
需要16个字节,所以最后一个值是正确的。