Önemli C uzantıları, davranışları makine sözcük boyutundan bağımsız olan tamsayı türlerini içeriyor mu?


12

C'nin diğer bazı dillerle karşılaştırıldığında ilginç bir özelliği, veri türlerinin çoğunun mutlak terimlerle belirtilmek yerine hedef mimarinin kelime boyutuna dayanmasıdır. Bu, dilin belirli türlerle zorluk yaşayabilecek makinelerde kod yazmak için kullanılmasına izin verirken , farklı mimarilerde tutarlı bir şekilde çalışacak kod tasarlamayı çok zorlaştırır. Kodu düşünün:

uint16_t ffff16 = 0xFFFF;
int64_t who_knows = ffff16 * ffff16;

int16 bit (hala birçok küçük mikrodenetleyici için geçerli) olan bir mimaride bu kod, iyi tanımlanmış davranışlar kullanarak 1 değerini atayacaktır. int64 bit olan makinelerde , yine iyi tanımlanmış davranışlar kullanarak 4294836225 değerini atayacaktır. int32 bit olan makinelerde , muhtemelen -131071 değeri atayacaktır (bunun Uygulama Tanımlı mı yoksa Tanımlanmamış Davranış mı olacağını bilmiyorum). Kod, nominal olarak "sabit boyutlu" türler olarak kabul edilenler dışında hiçbir şey kullanmasa da, standart bugün kullanılan iki farklı derleyicinin iki farklı sonuç vermesini ve bugün birçok popüler derleyicinin üçte birini vermesini gerektirecektir.

Bu belirli örnek, gerçek dünya kodunda iki 16 bit değerin ürününü doğrudan 64 bit değere atamasını beklemeyeceğim, ancak üç yollu tamsayıyı göstermek için kısa bir örnek olarak seçildi. promosyonlar sözde sabit boyutlu işaretsiz türlerle etkileşime girebilir. İmzasız tiplerde matematiğin matematiksel tamsayı aritmetiği kurallarına göre gerçekleştirilmesinin gerekli olduğu bazı gerçek dünya durumları, modüler aritmetik kurallarına göre yapılması gerektiği ve bazıları gerçekten yapmadığı bazı gerçek dünya durumları vardır. önemli değil. Sağlama toplamı gibi şeyler için birçok gerçek dünya kodu uint32_taritmetik sarma moduna 2³² ve keyfi performansa dayanmaya dayanıruint16_t en az doğru olan aritmetik ve get sonuçları, 65536 doğru mod olarak tanımlanmıştır (Tanımsız Davranışı tetiklemenin aksine).

Bu durum açıkça istenmeyen görünse bile (ve 64 bit işleme birçok amaç için norm haline geldikçe daha fazla olacak), gözlemlediğim şeyden C standartları komitesi, bazı önemli üretimde zaten kullanılan dil özelliklerini tanıtmayı tercih ediyor "sıfırdan" icat etmek yerine C dilinin sadece bir türün nasıl saklanacağını değil, aynı zamanda olası tanıtımları içeren senaryolarda nasıl davranması gerektiğini belirtmesine izin verecek dikkate değer uzantılar var mı? Bir derleyici uzantısının bu tür sorunları çözmesinin en az üç yolunu görebiliyorum:

  1. Derleyiciye belirli "temel" tamsayı türlerini belirli boyutlar olmaya zorlama talimatı veren bir yönerge ekleyerek.

  2. Derleyiciye, hedef mimarideki türlerin gerçek boyutlarından bağımsız olarak, makinenin türlerinin belirli boyutlara sahipmiş gibi çeşitli tanıtım senaryolarını değerlendirmesini söyleyen bir yönerge ekleyerek.

  3. Bir eklenmesi; (belirli özellikleri olan bir türü ne olursa olsun, altta yatan bir kelime büyüklüğü, bir mod-65536 sarma cebirsel halkası gibi davranması gerektiği ve diğer türlerdeki örtük olarak dönüştürülebilir olmamalıdır, örneğin declare tipleri bildirimi araçlarını izin vererek wrap32bir için intbir vermelidir türünün sonucu, 16 bit'ten büyük wrap32olup olmadığına bakılmaksızın, doğrudan bir a inteklemek yasadışı olmalıdır (çünkü ikisi de diğerine dönüşemez).wrap32wrap16

Benim tercihim üçüncü alternatif olurdu, çünkü alışılmadık kelime boyutlarına sahip makinelerin bile değişkenlerin iki boyuttan güçle olduğu gibi "sarmasını" bekleyen çok sayıda kodla çalışmasına izin verecekti; derleyici, türün uygun şekilde davranmasını sağlamak için bit maskeleme talimatları eklemek zorunda kalabilir, ancak kod mod 65536'yı saran bir türe ihtiyaç duyarsa, derleyicinin kaynak kodunu onunla karıştırmaktan ziyade bu tür maskeleme yapması daha iyidir. veya böyle bir maskeleme gereken makinelerde bu kodu kullanamazsınız. Yine de, yukarıdaki araçlardan herhangi biri aracılığıyla veya düşünmediğim bazı yollarla taşınabilir davranış elde edecek ortak uzantılar olup olmadığını merak ediyorum.

Aradığım şeyi netleştirmek için birkaç şey var; en önemlisi:

  1. İstenen anlambilimi sağlamak için kodun yazılabilmesi için birçok yol olsa da (örn. Gerçekleştirilecek makroları tanımlamak, belirli boyuttaki imzasız işlenenler üzerinde açık bir şekilde saran veya olmayan bir sonuç elde etmek için matematik yapmak) veya en azından istenmeyenleri önlemek semantik (örneğin bir tür şartlı tanımlamak wrap32_tolmak uint32_tbir derleyicilerde uint32_tterfi olmazdı ve figür daha iyi gerektirir kodu olduğunu wrap32_to tip çalıştırmak ve sahte davranış verim için daha terfi ediyorum makinelerde derleme başarısız), gelecekteki dil uzantıları ile en uygun olan kodu yazmanın herhangi bir yolu varsa, bunu kullanmak kendi yaklaşımımı tasarlamaktan daha iyi olurdu.

  2. Birçok tamsayı boyutu sorununu çözmek için dilin nasıl genişletilebileceğine dair oldukça sağlam fikirlerim var, kodun farklı kelime boyutlarına sahip makinelerde aynı anlambilimi vermesine izin veriyorum, ancak bunları yazmak için önemli bir zaman harcamamadan önce bu yönde hangi çabaların zaten yapıldığını bilmek.

Hiçbir şekilde C Standartları Komitesi'ni veya ürettikleri işi küçümseme olarak görülmek istemiyorum; Ancak, birkaç yıl içinde "doğal" tanıtım türünün 32 bit olacağı makinelerde ve 64 bit olacağı makinelerde kodun doğru şekilde çalışmasının gerekli olacağını umuyorum. Dile bazı mütevazı uzantılarla (C99 ve C14 arasındaki diğer değişikliklerin çoğundan daha mütevazı) düşünüyorum, sadece 64 bit mimarileri verimli bir şekilde kullanmak için temiz bir yol sağlamak mümkün olmayacak, aynı zamanda pazarlık ile etkileşimi de kolaylaştıracak "olağandışı kelimelik boyutlu" standardı olan 12-bit ile makinelerde mümkün kılar destek [örneğin tarihsel bükülmüş üzerinde geriye sahip olduğunu makineleri charbir bekler çalışma kodunauint32_tmod 2³² sarmak için]. Gelecekteki uzantıların alacağı yöne bağlı olarak, bugün yazılan kodun, varsayılan tamsayı türlerinin "beklenen" gibi davrandığı bugünün derleyicilerinde kullanılabilmesini sağlayacak, ancak tamsayıların gelecekteki derleyicilerinde de kullanılabilmesini sağlayacak makrolar tanımlamanın mümkün olmasını beklerdim. türler varsayılan olarak farklı davranır, ancak gerekli davranışları nerede sağlayabilir?


4
@RobertHarvey Emin misin? Tamsayı yükseltmeyi anladığım gibi , eğer intdaha büyükse uint16_t, çarpmanın işlenenleri yükseltilecek intve çarpma çarpma olarak gerçekleştirilecek intve elde edilen intdeğer int64_t, başlatılması için dönüştürülecektir who_knows.

3
@RobertHarvey Nasıl Yapılır? OP kodunda, bundan söz edilmiyor int, ancak yine de gizlice içeri giriyor. (Yine C standardını anladığımın doğru olduğunu varsayarsak.)

2
@RobertHarvey Kulağa kötü geldiğinden emin olun, ancak böyle bir yola işaret edemezseniz, "hayır, yanlış bir şey yapmalısınız" diyerek hiçbir şeye katkıda bulunmuyorsunuz. Asıl soru , tamsayı tanıtımından nasıl kaçınılacağı veya etkilerini nasıl aşacağıdır!

3
@RobertHarvey: C Standartları Komitesi'nin tarihsel hedeflerinden biri, hemen hemen her makinenin bir "C derleyicisi" olmasını mümkün kılmak ve kuralların, belirli bir hedef makine için bağımsız olarak geliştirilen C derleyicilerinin yeteri kadar spesifik olmasını sağlamak olmuştur. çoğunlukla değiştirilebilir olmak. Bu, insanların standartlar tasarlanmadan önce birçok makine için C derleyicileri yazmaya başlaması ve Standartlar Komitesi'nin derleyicilerin mevcut kodun güvenebileceği herhangi bir şey yapmasını yasaklamak istememesi nedeniyle karmaşıktı . Standardın oldukça temel bazı yönleri ...
supercat

3
... herkesin "mantıklı" bir takım kurallar formüle etmeye çalıştığı için değil, daha ziyade Komite zaten var olan bağımsız olarak yazılmış derleyicilerin ortak olduğu her şeyi çakmaya çalıştığı için olduğu gibidir . Ne yazık ki, bu yaklaşım aynı anda programcıların ne yapılması gerektiğini belirlemeye izin vermeyecek kadar belirsiz, ancak derleyicilerin "sadece yapmasına" izin vermek için çok spesifik olan standartlara yol açmıştır.
supercat

Yanıtlar:


4

Kodun tipik niyeti böyle

uint16_t ffff16 = 0xFFFF;
int64_t who_knows = ffff16 * ffff16;

çarpımı 64 bit (sonucun saklandığı değişkenin boyutu) olarak gerçekleştirmektir (platformdan bağımsız) doğru sonucu elde etmenin genel yolu, işlenenlerden birini 64-bit çarpmayı zorlamaktır:

uint16_t ffff16 = 0xFFFF;
int64_t i_know = (int64_t)ffff16 * ffff16;

Bu işlemi otomatik yapan hiçbir C uzantısıyla karşılaşmadım.


1
Benim sorum, belirli bir aritmetik ifadenin doğru değerlendirmesinin nasıl zorlanacağı değil (kişinin ne tür bir sonuca bağlı olduğuna, bir işleneni uint32_tya #define UMUL1616to16(x,y)((uint16_t)((uint16_t)(x)*(uint16_t)(y)))da #define UMUL1616to16(x,y)((uint16_t)((uint32_t)(x)*(uint16_t)(y)))olarak tanımlanmış bir makroya ya da boyutuna bağlı olarak bir makro kullanmasına bağlı olarak int) değil, kendi makrolarımı tanımlamaktansa bu tür şeyleri nasıl kullanacağınıza dair ortaya çıkan standartlar.
supercat

Ayrıca, karma ve sağlama toplamı hesaplamaları gibi şeyler için, amacın genellikle bir sonuç almak ve onu işlenenlerin boyutuna kısaltmak olacağını belirtmeliydim. Benzer bir ifadenin tipik amacı, (ushort1*ushort2) & 65535utüm işlenen değerleri için mod-65536 aritmetiği yapmak olacaktır. C89 mantığını okurken, yazarlar sonuç 2147483647'yi aşarsa bu tür kodların bazı uygulamalarda başarısız olabileceğini fark etseler de, bu tür uygulamaların giderek daha nadir hale gelmesini beklediklerini düşünüyorum. Ancak bu kod bazen modern gcc'de başarısız olur.
supercat
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.